<?php
/**
 * Created by PhpStorm.
 * User: guokunsheng
 * Date: 2018/12/27
 * Time: 15:29
 */

namespace App\Tools;

use app\astrologer\controller\Upload;
use App\Models\CacheModel\AppConfig;
use App\Models\CacheModel\Goods;
use App\Models\CacheModel\lucky\AppUserAdornInfo;
use App\Models\CacheModel\User;
use App\Models\CacheModel\ZlAdminVip;
use App\Models\CacheModel\zlcr2\Zlcr2UserQuestion;
use App\Models\CacheModel\ZlUserLoginRecord;
use App\Models\CacheModel\ZlUserZx;
use App\Tools\Astrolog\Astrolog;
use app\common\lib\exception\ApiException;
use App\Tools\horoscope\Horoscope as HoroscopeData;
use App\Models\Recode;
use app\zyhsapi\model\ZyhsConfig;
use app\zyhsapi\model\ZyhsRecode;
use app\zyhsapi\model\ZyhsRecodeAstro;
use app\zyhsapi\model\ZyhsUser;
use app\zyhsapi\model\ZyhsUserZx;
use Exception;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use OSS\Core\OssException;
use RabbitmqRectificationProducer;
use Cache;
use Illuminate\Routing\Controller;
use Db;
use Log;
use Queue;
use AstroTool\Cache\CacheManage;
use AstroTool\curl\Curl;


/**
 * 扩展工具类库
 * Class Util
 * @package app\common\lib
 */
class Util extends Controller
{

    /**
     * 检查验证码短信是否存在盗刷，本次只记录日志
     * @param $request
     */
    public static function checkGetMsgCodeRequest($request): void
    {
        $log_pre = '检查短信验证码是否存在盗刷-';
        try
        {
            $headers = $request->header();
            // 基础参数校验
            if (isset($headers['app-type']))
            {
                if (strpos(config('app.app_types'), $headers['app-type']) !== false)
                {   //APP
                    if (!isset($headers['sign']))
                    {
                        Log::write($log_pre . '请求缺少sign', 'log', true);
                    }
                    //解密sign
                    $str = (new Aes())->decryptWithOpenssl($headers['sign']);

                    if (empty($str))
                    {
                        Log::write($log_pre . '解密sign失败：sign=' . $headers['sign'] . '，解密后：$str=' . $str, 'log', true);
                    }
                    //把string转换成arr
                    parse_str($str, $arr);

                    // 临时更换 did验证
                    if (!is_array($arr)
//            || !isset($arr['did']) || $arr['did'] !== $data['did']
                        || !isset($arr['version']) || $arr['version'] !== $headers['version']
                        || (($headers['app-type'] !== 'android' || $headers['version'] !== '1.9.8') && (!isset($arr['did']) || $arr['did'] !== $headers['did']))
                    )
                    {
                        Log::write($log_pre . 'sign验签失败：header里的信息和解密后的对应不上', 'log', true);
                    }

                    //上面是通用的校验，下面是实际的校验时间的方法
                    $header_timestamp = (int)$headers['time'];
                    $now_timestamp = time();
                    $time_sub = abs($header_timestamp - $now_timestamp);
                    //超过10秒的先打印日志，看看能不能过滤掉，刷短信的那个sb
                    if ($time_sub > 60)
                    {
                        $illegalDid = ['ffffffff-b22b-b531-ffff-fffff9ef3f8d'];//忽略did列表
                        if (!in_array($headers['did'], $illegalDid, true))
                        {
                            Log::write($log_pre . 'header时间戳超过了10秒钟，header里的时间戳=' . $header_timestamp . '[' . date('Y-m-d H:i:s', $header_timestamp) . ']' . '，sign里的时间戳=' . $arr['time'] . '[' . date('Y-m-d H:i:s', $arr['time']) . ']' . '，当前时间戳=' . $now_timestamp . '[' . date('Y-m-d H:i:s', $now_timestamp) . ']' . '，时间戳差额=' . $time_sub . '秒，params=' . json_encode($request->param(), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . ',headers=：' . json_encode($headers, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), 'log', true);
                        }

                    }
                } else
                {
                    Log::write($log_pre . '当前设备不支持：app-type=' . $headers['app-type'], 'log', true);
                }
            } else
            {
                Log::write($log_pre . '当前设备不支持', 'log', true);
            }
        }
        catch (\Exception $e)
        {
            Log::write($log_pre . '失败：error=' . $e->getMessage() . ',line=' . $e->getLine(), 'log', true);
        }
    }

    /**
     * 获取用户的装扮
     * @param $uid 用户的数据
     * @return array
     */
    public static function getUserAdorn($uid): array
    {
        try
        {
            $adorn = (new AppUserAdornInfo())->getUserAdornInfo($uid, self::getUserType($uid));
            $res = [
                'avatar_adorn' => $adorn['adorn_info']['reality_img'],
                'tag' => $adorn['tag_info']['reality_img'],
                'badge' => array_column($adorn['badge_info'], 'reality_img'),
            ];
        }
        catch (\Exception $e)
        {
            Log::write('获取用户装扮失败（返回的默认空的数据）：uid=' . $uid . '，error:' . $e->getMessage(), 'log', true);
            $res = [
                'avatar_adorn' => '',
                'tag' => '',
                'badge' => [],
            ];
        }
        return $res;
    }

    /**
     * 获取用户类型（用于 (new AppUserAdornInfo())->getUserAdornInfo【获取用户 当前的装扮信息】）【0=>普通用户 ，1=>官方, 2=>女神】
     */
    public static function getUserType($uid): int
    {
        //查询wechat.zl_admin_vip表，4为官方，5为女神；
        $user_type = 0;

        try
        {
            //查询是否为官方账号
            $officialUserIds = (new ZlAdminVip())->getVipInfo(4);
            if (in_array($uid, $officialUserIds, false))
            {
                $user_type = 1;
            }
            //查询是否为女神账号
            $goddessUserIds = (new ZlAdminVip())->getVipInfo(5);
            if (in_array($uid, $goddessUserIds, false))
            {
                $user_type = 2;
            }
        }
        catch (\Exception $e)
        {
            Log::write('获取用户类型失败（返回的用户类型为0）：error:' . $e->getMessage(), 'log', true);
            $user_type = 0;
        }


        return $user_type;
    }


    /**
     * 判断当前环境是否是online
     * @return bool
     */
    public static function isOnline(): bool
    {
        return Env::get('host.name') === 'online';
    }

    /**
     * 通过档案获取星盘/合盘的url地址
     * @param $headers
     * @param array $recodes 档案列表
     * @return string
     */
    public static function getAstroUrlFromRecords($headers, array $recodes = []): string
    {
        //星盘地址前缀
        $astro_domain = Env::get('host.astro_domain');
        $access_token = $headers['access-token'] ?? "";//已经绑定了用户，那么把 access_token 传过去
        if (isset($recodes['recode2']))
        {//合盘
            $astro_url = $astro_domain . '/synastry_astro?tab_index=0&record1=' . urlencode(json_encode(self::getAstroCustomRecord($recodes['recode1']))) . '&record2=' . urlencode(json_encode(self::getAstroCustomRecord($recodes['recode2']))) . '&access_token=' . base64_encode($access_token);
        } else
        {//星盘
            $astro_url = $astro_domain . '/astro?tab_index=1&record1=' . urlencode(json_encode(self::getAstroCustomRecord($recodes['recode1']))) . '&access_token=' . base64_encode($access_token);
        }

        return $astro_url;
    }

    /**
     * 获取星盘自定义的档案信息
     * @param array $recode
     * @return array
     */
    public static function getAstroCustomRecord(array $recode = []): array
    {
        try
        {
            return [
                "year" => $recode['year'],
                "month" => $recode['month'],
                "day" => $recode['day'],
                "hour" => $recode['hour'],
                "minute" => $recode['minute'],
                "timezone" => $recode['timezone'],
                "long_deg" => $recode['long_deg'],
                "long_min" => $recode['long_min'],
                "ew" => $recode['ew'],
                "lat_deg" => $recode['lat_deg'],
                "lat_min" => $recode['lat_min'],
                "ns" => $recode['ns'],
                "is_summer" => $recode['is_summer'],
            ];
        }
        catch (\Exception $e)
        {
            Log::write('获取星盘自定义的档案信息失败：error:' . $e->getMessage(), 'error', true);
            return [];
        }

    }

    public static function getZhunleWapLiverRedirectUrl($headers, $params): string
    {
        $page = $params['page'];
        $h = $params['h'];
//        请求前缀：$HOST/live/v29/liver_admin_page?zhunle_not_show_nav=1&h=【int 状态栏高度，参考意见反馈的参数】&need_auth=2【必须登录】
//我的主播列表：
//liver_page?page=my_liver_list
//    直播记录列表：
//liver_page?page=live_list
//    直播记录详情：
//liver_page?page=live_detail&id=【int 记录id】
//数据总览记录详情：
//liver_page?page=live_detail_all
//    赠送及抽奖记录列表：
//liver_page?page=live_award_or_give_list&type=【int 1=>抽奖列表 2=>优惠券赠送列表】
//赠送及抽奖记录详情：
//liver_page?page=live_award_or_give_detail&id=【int 赠送及抽奖记录id】&type=【int 1=>抽奖详情 2=>赠送详情】
//    --------下面是新增的--------
//    主播用户中心（目前用于后台管理）
//liver_page?page=liver_user_center&live_id=【主播id】
//    解答列表：
//liver_page?page=answer_list&live_record_id=【直播记录id】&type=【int 1=>收费 2=>免费】
//解答详情：
//liver_page?page=answer_detail&id=【解答记录id】&type=【  1=>直播间看盘，2=>直播间骰子，3=>直播间塔罗】
        $params_str = '';
        switch ($page)
        {
            case 'live_list':
            {
                $params_str = 'page=' . $page;
                break;
            }
            case 'live_detail':
            {
                $params_str = 'page=' . $page;
                $id = $params['id'];
                if ($id >= 0)
                {
                    $params_str .= '&id=' . $id;
                }
                break;
            }
            case 'live_award_or_give_list':
            {
                $type = $params['type'];
                if ($type >= 1)
                {
                    $params_str = 'page=' . $page . '&type=' . $type;
                }
                break;
            }
            case 'live_award_or_give_detail':
            {
                $id = $params['id'];
                $type = $params['type'];
                if ($id)
                {
                    $params_str .= 'id=' . $id;
                }
                if ($type)
                {
                    $params_str .= '&type=' . $type;
                }
                break;
            }
            case 'liver_user_center':
            {
//                liver_page?page=liver_user_center&live_id=【主播id】
                $live_id = $params['live_id'];
                if ($live_id)
                {
                    $params_str .= '&live_id=' . $live_id;
                }
                break;
            }
            case 'answer_list':
            {
                $params_str .= 'page=' . $page;
                $live_record_id = $params['live_record_id'];
                $type = $params['type'];
                if ($live_record_id)
                {
                    $params_str .= '&live_record_id=' . $live_record_id;
                }
                if ($type)
                {
                    $params_str .= '&type=' . $type;
                }
                break;
            }
            case 'answer_detail':
            {
                $params_str .= 'page=' . $page;
                $id = $params['id'];
                $type = $params['type'];
                if ($id)
                {
                    $params_str .= '&id=' . $id;
                }
                if ($type)
                {
                    $params_str .= '&type=' . $type;
                }
                break;
            }
            default:
                $params_str = 'page=' . $page;
        }

        if ('' !== $params_str)
        {
            $params_str .= '&';
        }

        $url = Env::get('host.wap_domain') . '/zhunle_wap_liver?' . $params_str . 'token=' . $headers['access-token'] . '&app_type=' . $headers['app-type'] . '&did=' . $headers['did'] . '&model=' . $headers['model'] . '&sign=' . $headers['sign'] . '&time=' . $headers['time'] . '&version=' . $headers['version'] . '&phone_version=' . $headers['phone-version'] . '&h=' . $h;

        return $url;
    }

    /**
     * 获取占星师升级提示
     * @param int $level 当前占星师的等级 0=>专业占星师,1=>高级占星师,2=>大师占星师,3=>至臻占星师
     * @param int $grade 当前占星师的能力分
     * @return string
     */
    public static function getAstrologerLevelUpDes($level = 0, $grade = 0)
    {
//专业占星师： 能力分60-99分
//完成订单分成20%（税前）
//高级占星师： 能力分100-499分
//完成订单分成30%（税前）
//大师占星师： 能力分500-1499分
//完成订单分成40%（税前）
//至臻占星师： 能力分1500分以上
//完成订单分成50%（税前）
        $grades = [0, 100, 500, 1500];
        $levels = ['专业占星师', '高级占星师', '大师占星师', '至臻占星师'];
        $earn_percents = ['20%', '30%', '40%', '50%'];
        if ($level < 3)
        {
            return '距离进阶' . $levels[$level + 1] . '只差' . ($grades[$level + 1] - $grade) . '能力分，' . $levels[$level + 1] . '分成比例' . ($earn_percents[$level + 1]);
        }

        return '您已经成为至臻占星师，分成比例50%';
    }

    /**
     * 判断是否是手机浏览器
     * @return bool
     */
    public static function isMobile()
    {
        // 如果有HTTP_X_WAP_PROFILE则一定是移动设备
        if (isset($_SERVER['HTTP_X_WAP_PROFILE']))
        {
            return true;
        }
        // 如果via信息含有wap则一定是移动设备,部分服务商会屏蔽该信息
        if (isset($_SERVER['HTTP_VIA']))
        {
            // 找不到为flase,否则为true
//            return stristr($_SERVER['HTTP_VIA'], "wap") ? true : false;
            return stripos($_SERVER['HTTP_VIA'], "wap") !== false ? true : false;
        }
        // 脑残法，判断手机发送的客户端标志,兼容性有待提高。其中'MicroMessenger'是电脑微信
        if (isset($_SERVER['HTTP_USER_AGENT']))
        {
            $clientkeywords = ['nokia', 'sony', 'ericsson', 'mot', 'samsung', 'htc', 'sgh', 'lg', 'sharp', 'sie-', 'philips', 'panasonic', 'alcatel', 'lenovo', 'iphone', 'ipod', 'blackberry', 'meizu', 'android', 'netfront', 'symbian', 'ucweb', 'windowsce', 'palm', 'operamini', 'operamobi', 'openwave', 'nexusone', 'cldc', 'midp', 'wap', 'mobile', 'MicroMessenger'];
            // 从HTTP_USER_AGENT中查找手机浏览器的关键字
            if (preg_match("/(" . implode('|', $clientkeywords) . ")/i", strtolower($_SERVER['HTTP_USER_AGENT'])))
            {
                return true;
            }
        }
        // 协议法，因为有可能不准确，放到最后判断
        if (isset ($_SERVER['HTTP_ACCEPT']))
        {
            // 如果只支持wml并且不支持html那一定是移动设备
            // 如果支持wml和html但是wml在html之前则是移动设备
            if ((strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') !== false) && (strpos($_SERVER['HTTP_ACCEPT'], 'text/html') === false || (strpos($_SERVER['HTTP_ACCEPT'], 'vnd.wap.wml') < strpos($_SERVER['HTTP_ACCEPT'], 'text/html'))))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * 占星师平台敏感信息解密/加密
     * @param array $data 要加密或者解密的数组
     * @param bool $is_decode 是否解密，默认 是
     * @return array|mixed
     */
    public static function astrologerSensitiveInfoDecodeOrEncode($data = [], $is_decode = true)
    {
        //需要解密的key
        $keys_encode = ['name', 'telphone', 'id_card', 'left_pic_info', 'right_pic_info', 'bank_card', 'bank_name', 'bank_branch_name', 'bank_id_card', 'bank_id_card_name', 'bank_info', 'bank_branch_info'];

        foreach ($data as $k => $v)
        {
            if (in_array($k, $keys_encode))
            {
                if ($is_decode)
                {
                    $data[$k] = self::decryptWithOpenssl($v);
                } else
                {
                    $data[$k] = self::encryptWithOpenssl($v);
                }
            }
        }

        return $data;
    }

    /**
     * 占星师平台敏感信息 解密字符串
     * @param string $data 字符串
     * @return string
     */
    public static function decryptWithOpenssl($data): string
    {
        $config = config('simulation_astrologer');
        return openssl_decrypt(base64_decode($data), $config['aes_method'], $config['aes_key'], OPENSSL_RAW_DATA, $config['aes_iv']);
    }

    /**
     * 占星师平台敏感信息 加密字符串
     * @param string $data 字符串
     * @return string
     */
    public static function encryptWithOpenssl($data): string
    {
        $config = config('simulation_astrologer');
        return base64_encode(openssl_encrypt($data, $config['aes_method'], $config['aes_key'], OPENSSL_RAW_DATA, $config['aes_iv']));
    }

    /**
     * 获取档案的日月升信息名称
     * @param $astro
     * @return string
     */
    public function recodeAstro($astro)
    {
        $astro = explode('-', $astro)[0];
        // 获取星座名称配置
        $sign_name = config('astrolog.sign_name');
        //
        $sign = (int)($astro);
        if (in_array($sign, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], true))
        {
            return explode('|', $sign_name[$sign])[3];
        }
        return '';
    }

    /**
     * 获取档案的日月升信息名称
     * @param $astro
     * @return string
     */
    public function recodeAstroShorthand($astro)
    {
        $astro = explode('-', $astro)[0];
        // 获取星座名称配置
        $sign_name = config('astrolog.sign_name');
        //
        $sign = (int)($astro);
        if (in_array($sign, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], true))
        {
            return explode('|', $sign_name[$sign])[2];
        }
        return '';
    }

    /**
     * 获取档案的日月升信息名称
     * @param $astro
     * @return string
     */
    public function recodeAstroMiddlehand($astro)
    {
        $astro = explode('-', $astro)[0];
        // 获取星座名称配置
        $sign_name = config('astrolog.sign_name');
        //
        $sign = (int)($astro);
        if (in_array($sign, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], true))
        {
            return mb_substr(explode('|', $sign_name[$sign])[3], 0, 2);
        }
        return '';
    }

    /**
     * 获取星座名
     * @param $sign_id
     * @return array
     * @throws ApiException
     */
    public function signTypeName($sign_id)
    {
        switch ($sign_id)
        {
            case 1:
                $sign_name = '白羊座';
                $date = '3.21-4.19';
                $des = '　　白羊座是黄道和春天的第一个星座。春回大地，万物的生机都表现在白羊座的身上。春天的气息赋予白羊座崭新生命力，生机勃勃，激情盎然，白羊座需要毫不吝惜地去燃烧自己激情能量。不搞权宜之计，不委曲求全，也从不注意细节。白羊座的人的优点是当机立断，付之行动和速战速决。如果上升、太阳或月亮在这一星座中，那么你会是一个朝气蓬勃、热情坦率、慷慨真诚、坚强勇敢、动作敏捷和有些固执已见的人。不足之处是，你说话做事不太审慎，很少注意留有余地，缺乏冷静的头脑和周密的思索。
　　白羊座喜欢无拘无束和自行其是，而不愿意步他人之后尘，白羊座从来不掩饰自己的感情，要么热情洋溢，要么怒发冲冠。如果白羊座的愿望受阻，他也决不悄然收兵。无论是在家里还是在外面，白羊座都不怕争执，但事后总是弃之脑后，从不记恨在心。在困难和危险的关头，白羊座能充分表现出自己的品格和勇气，得到人们的敬佩和赞扬。白羊座做事从不吝惜气力，宁可付出巨大的代价，也要力争前茅。总之，白羊座从来不在任何困难和失败面前低头。';
                break;
            case 2:
                $sign_name = '金牛座';
                $date = '4.20-5.20';
                $des = '　　在充满激情的白羊座后面的，是不轻易浪费自己能量的金牛座。金牛座不愿意毫无意义地说教，无缘无故地行动和失去理智激动。金牛座是一个强烈抑制精神和思想总是按一定尺度运行的星座，如果星盘中有较强的金牛座的影响，那么你会有良好的自卫本能。思想一成不变，比较罗嗦。金牛座的性格平稳、有毅力和耐力，勤劳智慧，富有实干精神。为人处世小心谨慎，感情真诚专一。此外，金牛座有极其敏锐的感官，内心怀有各种欲望。喜欢舒适的生活环境，大自然的壮丽景色、花草和动物。
　　金牛座是喜欢按自己的人生哲学走路的星座，不轻易改变自己的生活习惯。固执已见是金牛座性格上的突出特点，同时也是金牛座的主要缺点。平时温文尔雅，一旦受到触怒，金牛座会变得令人望而生畏。金牛座家庭观念较强，把家庭作为寄托自己幸福和安居乐业生活的可靠圣殿。金牛座爱孩子胜过一切，并对他们寄予厚望。';
                break;
            case 3:
                $sign_name = '双子座';
                $date = '5.21-6.21';
                $des = '　　来无影、去无踪、心神不定、脚步不停，这便是金牛座之后是轻率多变的双子座。这是一个兴趣广泛并愿承担传播、普及信息使命的星座。双子座的人有典型的大城市人气质，生活节奏快，每天有各种各样的活动和安排，但有时缺乏远见，在人生的旅途中双子座会经历坎坷。然而每每这个时候，他都能找到妥善的办法，驾驭形势，摆脱困境。
　　如果星盘重要行星与双子座有关，那么你可能是一个追求自由意识、生性单纯，不注重生活细节的人。活泼、热情、富有浪漫气质与想象力。接受并适应新事物的能力强。知识广博，口齿伶俐，善于思维和表达，具有幽默感。此外，你还是一个敢闯敢干的人，有多种成功之路。你的见解往往充满智慧，这使你成为一个有发明创造和有开拓精神的人。双子座的你思维敏捷，但有时也会缺乏冷静的权衡，需要不断地改换环境，例如外出旅行、与别人交流思想，或者在各个方面表现自己，否则你就会感到烦躁不安。你聪明伶俐，有些轻率和神经质，常常沉缅于令人难以理解的意念之中，只喜欢做感兴趣和使自己开心的事。';
                break;
            case 4:
                $sign_name = '巨蟹座';
                $date = '6.22-7.22';
                $des = '　　巨蟹座是夏天开始的第一个星座，夏天把深居简出、思想保守和敏感的性格带给了这一星座，这是个需要自我保护的星座。出生在这一星座的人，有慈母(慈父)般的热情，也洋溢着孩子般的纯洁和天真，有很强的制力。巨蟹座的人生哲学是：使自己和别人都幸福如愿。巨蟹座对自己用双手创造出来的有限幸福心满意足，但总担心有人会夺去这一幸福，因此，巨蟹座过分地抓住他在精神和物质上所拥有的一切。这一星座的人的优点是热情、正直、谦虚、谨慎、头脑冷静、感情细腻、为人真诚、忠心耿耿、富有组织才能、既有耐心又有毅力。巨蟹座的人的弱点是性格被动、思想保守、依赖性强、多疑多虑、容易制造惊慌不安的气氛，而在真正危险面前往往不知所措。这一星座的人或者食欲过盛、或者食欲缺乏。另外，固执、疑虑和沙文主义也是夏天出生的巨蟹座的人主要缺点。如果受到月亮和海王星不佳相位的影响，某些巨蟹座的人易受梦幻般漂泊生涯的吸引，其命运是莫测的，有时甚至是悲剧性的。
　　超群的直觉和敏感是巨蟹座的主要性格特征。这一星座的人多半喜欢生活在旖旎的幻想中，颇有些放梦境于海浪，寄情思于蓝天的意境。巨蟹座不大适应旋尔即逝的生活节奏，而偏爱安谧的环境以及一切能唤起他想象和感受的气氛。这是一个把人生当作唯一美的星座，巨蟹座不愿直对生活中的矛盾和冲突，在内心里和现实中他都尽力把自己这些纷繁之事隔绝开来。';
                break;
            case 5:
                $sign_name = '狮子座';
                $date = '7.23-8.22';
                $des = '　　狮子座是夏天的第二个星座。炎热的夏天赋予狮子座的人果敢和胆略。狮子座严于律已亦严于律人，有时过分地相信自己的力量和优势。这是一个能成就大事业的干才。狮子座主要特点是思想开放，竭尽全力冲破自己能量的极限，战胜艰难险阻，去开创光辉灿烂的新局面。狮子座的人风度翩翩，引人注目，他有宏伟的志向、坚韧不拔的毅力、所向披靡的竞争力，同时胆识过人，为人襟怀坦荡，宽宏大量，热情洋溢。狮子座的思想中经常闪烁着英雄主义和理想主义的火花。
　　狮子座的弱点是高傲、过于敏感和坦诚。正如神话故事所描绘的国王一样，威严、宽厚、仁慈而且高傲。狮子座的内心沸腾着强烈的激情，浑身充满活力和生机，他做事与考虑问题有时缺乏谨慎态度，不知留余地或过于简单化。容易被表象所迷惑，以致犯教条主义错误。狮子座特别需要愉快的心情。消遣和娱乐、参加各种社交活动、与朋友交往，并能在这些活动中表现出自己举足轻重的地位，否则，他就会变得忧郁寡欢，失去他的魅力和光彩(这是狮子座取得成功及吸引人的重要精神支柱)。休息会使狮子座感到疲劳，他最好是在频繁的活动中去放松自己。';
                break;
            case 6:
                $sign_name = '处女座';
                $date = '8.23-9.22';
                $des = '　　处女座是不惹人注目的效率冠军，是谦虚的典范。处女座的人对每一件事情都要周密计划，仔细安排，喜欢事情的来龙去脉一目了然，事后做记录以备查，每项开支都要记入帐目。总之，处女座的生活是一丝不苟和井井有条的，不喜欢突如其来的事情扰乱他的生活。尤其是当星盘中太阳以外的某一个或几个行星出现在处女座时，这种性格特点就更为明显。处女座的人总是试图将自己的激情和冲动置于理智的控制之下。处女座深思熟虑、反复斟酌，有时甚至放过了有利时机。处女座可爱、懦弱、常常为没有达到尽善尽美而感到内疚和不安，特别害怕自己上当受骗。
　　如果太阳在处女座的人出生的时候，没有受到太阳，木星或火星的重大相位的影响，那么他将会满足于一般的，但可靠的成功状态，而不大愿高瞻远瞩，奋勇拼搏，因为未知的一切都会使他感到担心。处女座要求自己的工作要做得无懈可击，需要别人感到他是不可缺少的，这些在你看来要比雄心壮志更为重要。这一星座的人具有勤劳的本色和极强的责任感。处女座特别适合从事需要细致、踏实精神的一切工作。一般说来，他的这一精神会在不需要他创新，不需要别人监督或从属的工作岗位上得到充分的发扬。据统计，相当数量的显要人物的得力助手、秘书和模范职员都是出生在处女座。';
                break;
            case 7:
                $sign_name = '天秤座';
                $date = '9.23-10.23';
                $des = '　　天秤座是象征着秋天来临的星座，秋意表现在天秤座的人身上是对意气相投的特殊嗅觉。天秤座寻求着共同点和互相谅解的土壤。和蔼可亲的秉性，使仇恨和敌意永远无法靠近天秤座的身旁。天秤座的人温柔、娴雅，他需要欢欢乐乐地生活，需要忠贞不渝的友谊和爱情。随和与顺从是天秤座性格上的主要特点。无论对天秤座的男性还是女性，都可以看到这些难能可贵的品质，品格正直，平易近人，处处闪耀着人格魅力的光辉，以及他们所蕴藏的艺术上的灵感和才华，这就是天秤座的特点！微不足道的事情就会使天秤座感到惊慌不安，他总是在寻找着内心的稳定与平衡，没有这种平衡他就不能平静地生活，这是一个能为生活中的矛盾开辟和解之路的星座。
　　秋天出生的天秤座举止言行十分注意分寸。和他相处既不可急躁，又不可简单粗暴，因为他的感情很难预测，高兴起来有时忘乎所以。天秤座对周围发生的一切都兴致勃勃，不仅知道事情的来龙去脉，而且还知道什么时机对成功最有利。天秤座的人最善于发现哪些人与他志趣相投。天秤的优点是：富有魅力、温文尔雅、性格平稳、目光敏锐，具有合作精神。不足之处是优柔寡断，缺乏坦率、难于理解，过分追求高雅的生活，因循守旧、机会主义、注重琐事、不专心和缺乏坚定性。天秤座需要别人的钦佩和赞扬，只有在他感到欣然的环境中，他才能无忧无虑地生活。';
                break;
            case 8:
                $sign_name = '天蝎座';
                $date = '10.24-11.22';
                $des = '　　天蝎座对互不相同的和互不相融的事物有特殊的兴趣，喜欢探究事物的真正并加以区别在萧瑟的秋风中降生到这一星座的人粗犷而倔强，天蝎座紧张的生活节奏会使接近他的人感到迷惘。天蝎座感情中常充满着矛盾。感情上的背叛对他是绝对不能忍受的，而且会使对他爱情产生与原来相对立的看法。天蝎有一双极其敏锐的眼睛，能洞察他人的弱点和机遇的利弊。另外，天蝎的神秘性、选择性、好斗性、狂热性和不妥协精神，也常常给人们留下深刻的印象。无法摆脱的烦恼常常纠缠着天蝎座，使他感到精疲力竭。强烈，是天蝎座的人的突出性格表现。巨大的耐力使他敢于迎接艰难险阻，并能征服之。无论生活中的还是情感方面的错综复杂的问题，非但不会使他厌烦，相反还会给他的生活增添乐趣。
　　天蝎座需要经常不断地处于忙碌之中。他喜欢亲自动手去做；喜欢改善自己的工作和生活环境；喜欢更新自己的想法，而不喜欢无所事事和庸庸碌碌的生活，那会使他丧失生机和活力。天蝎从不接受任何失败，如果遭到了挫折，他将会产生强烈的心理变态反应。而后他会从零开始，凭着顽强的意志和坚韧不拔的精神，重新奔向成功。';
                break;
            case 9:
                $sign_name = '射手座';
                $date = '11.23-12.21';
                $des = '　　射手座性格开朗，思想活跃，注重文化修养，同时又不忘放眼世界。常常射手座他人虽然在现实生活，但思想常常飞向遥远的过去和美好的未来。敏捷的思想跳跃着，一会在这，一会儿又在那，使人觉得射手座近在眼前，又仿佛在天边；让其他人觉得既与他志同道合，又仿佛与他格格不入。射手座是一个思想需要设法平静的星座。射手座热情洋溢，对生活充满火热的激情。他从不计较个人的得失，喜欢同时投身到许许多多的事情当中去，但轻率行为往往会给他带来烦恼。不论是在思想上还是在行动上，射手座随时都准备去经历风险。射手座对人生、未来和爱情的乐观情绪使他永葆青春。在人生道路上，射手座所做的一切努力都是为了使人们摆脱困境。此外，乐观主义精神，健康的体魄和快乐的情绪会给他带来运气和广泛的好感。射手座很善于安慰和鼓舞自己与周围的人，并振奋他们的精神。
　　射手座一般对世界上发生的一切事情都有浓厚的兴趣，喜欢外出旅行、好友善交。射手座深受众望，宽宏大量，但不希望别人威胁和干涉他的神圣自由。射手座讨厌义务，宁肯抛弃既得的利益，也不愿意为之受束缚。他喜欢家里经常高朋满座，并尽自己所能去帮助他们，他会因此结识许多社会上有影响的人物，在他的生活和事业上永远会得到支持或保护。';
                break;
            case 10:
                $sign_name = '摩羯座';
                $date = '12.22-1.19';
                $des = '　　魔羯座是象征着冬天开始的星座。冬天把绝对意识毫无保留地奉献给了魔羯座出生的人。太阳在魔羯座的人，尤其当有几个行星同时处于摩羯星座时，他将是一个具有现实主义思想和有抱负的人；同时他又容易被热烈的感情征服，是一个有强烈的忘我精神的人。魔羯座表情平静而淡漠，不太容易接近，喜欢离群独处，他害怕别人毫无意义的谈话会占据你宝贵的时间，也不能接受别人对他粗暴无礼。魔羯座严肃认真、思想深沉、始终如一、忠诚可靠、正直廉洁并富有献身精神。另外，魔羯座的人坚如磐石、毫不妥协，不姑息宽容，也从不报任何幻想。魔羯座深居简出，喜欢把自己关在象牙塔中，这对他比去经历激情的洗礼要好得多。谨慎可能会使他错过许多良机，于是他便会产生失望和怀才不遇之感。然而，魔羯座确实是个有强烈责任感的、勤奋的劳动者和杰出的组织者。如同攀登山峰一样，魔羯座的人的光辉前程，需要用坚持不懈的努力去换取。魔羯座的成功主要靠他艰苦奋斗的精神和他的工作能力，而不能指望运气的恩赐。魔羯座不喜欢碌碌无为，无所事事，他的实际感促使你不断地改进自己的工作质量和生活环境。';
                break;
            case 11:
                $sign_name = '水瓶座';
                $date = '1.20-2.18';
                $des = '　　水瓶座常常不考虑事情的某些具体方面，而一味推动它们的前进。水瓶座是一个富有开拓精神的星座。水瓶座思维能力高于本能，是个先锋派人物，他感兴趣的不是昨天而是明天。水瓶座喜欢坐超音速的“协和”号飞机，而不愿骑自行车。他迷恋斯特劳斯优美的旋律，而不喜欢听莫扎特的小夜曲。缺乏现实观念，当他沉浸在某一念头中的时候，会废寝忘食。人们争相与水瓶座交往，是因为他的创新精神和开拓思想，看事物的积极方式、理想主义、宽宏大量和生来就具有的高尚品德。但是水瓶座缺乏现实的态度、狂热、生来就不会平静地生活和凡事心中无数，这终究会使人们感到厌倦。
　　水瓶座也是一个个性极强的星座，他向往人之间美好的情意，但绝不愿意受感情上的丝毫束缚。水瓶座喜欢丰富自已的思想境界，喜欢到旅行中去开阔自己的视野，并喜欢在与人交往之中了解他们的思想观点。水瓶座的人不能忍受任何约束，他也决不强迫自己去服从任何纪律。如果某件事引起了他的兴趣，他能为之付出巨大的努力，单调无味的生活会使他心烦意乱，甚至能产生一种使他周围的人都无法忍受的变态心理。水瓶座时而异想天开，幽默过人，时而又冷若冰霜，令人费解，这常常是一个不易相处的人。水瓶座不能生活在谎言之中，过分耿直坦率，会被人认为是个怪人，其实人们对他并不真正了解。';
                break;
            case 12:
                $sign_name = '双鱼座';
                $date = '2.19-3.20';
                $des = '　　双鱼座有自己独特的缄默方式。他对世界上发生的一切，乃至虚无缥缈的事物都有浓厚的兴趣。这种琢磨不透的思想使他变成一轮神秘的光晕，吸引着许许多多的人。人们觉得双鱼座安静、温柔、真挚和会体贴人，但又觉得他的思想是那么深远莫测。双鱼座对愿望、激情和仇恨不像有些星座那样充满绝对意识，心中很少有“紧迫感”的概念，他不明白为什么明天也可以继续做的事，一定要抓紧时间今天做完。双鱼座的身体很容易疲劳，噪声、熙熙攘攘的人群、匆忙以及紧迫的生活节奏，都会使他精疲力竭。如果在太阳双鱼座的人的星盘中没有更富激励性的火星或天王星方位的影响，那么，他的漫不经心会变成惰性。
　　双鱼座的身上总保持着一种天真、忠厚的气质。一般地说，如果太阳在双鱼座的人的星盘中没有土星过多的不利因素影响，且又遇到木星或金星处在理想方位的情况时，他会经常受到幸运的照应：意想不到的事情、突如其来的机遇、喜幸的性格将会有利于他。他也有可能中彩或在人生道路上遇到一位非常富有的和慷慨的知已。于是，这位双鱼座的人就会进入另一种新的意境中，过着充满幻想或神秘色彩的美好生活；另一种趋向是他对生财之道表现得完全一无所知，或许有时会沾染上不诚实的毛病，这主要取决于木星和海王星在他的星盘中所处的位置如何。';
                break;
            default :
                throw new ApiException('未知的星座');
        }
        return ['name' => $sign_name, 'date' => $date, 'des' => $des];
    }

    /**
     * 获取行星名
     * @param $planet_id
     * @return string
     */
    public function PlanetTypeName($planet_id)
    {
        switch ($planet_id)
        {
            case 0:
                $planet_name = '太阳';
                break;
            case 1:
                $planet_name = '月亮';
                break;
            case 2:
                $planet_name = '水星';
                break;
            case 3:
                $planet_name = '金星';
                break;
            case 4:
                $planet_name = '火星';
                break;
            case 5:
                $planet_name = '木星';
                break;
            case 6:
                $planet_name = '土星';
                break;
            case 7:
                $planet_name = '天王星';
                break;
            case 8:
                $planet_name = '海王星';
                break;
            case 9:
                $planet_name = '冥王星';
                break;
            case 10:
                $planet_name = '凯龙星';
                break;
            case 11:
                $planet_name = '莉莉斯';
                break;
            case 12:
                $planet_name = '北交点';
                break;
            case 13:
                $planet_name = '婚神星';
                break;
            case 14:
                $planet_name = '福点';
                break;
            case 15:
                $planet_name = '宿命点';
                break;
            case 16:
                $planet_name = '南交点';
                break;
            case 17:
                $planet_name = '上升';
                break;
            case 18:
                $planet_name = '天底';
                break;
            case 19:
                $planet_name = '下降';
                break;
            case 20:
                $planet_name = '天顶';
                break;
            default:
                $planet_name = '';
                break;
        }
        return $planet_name;
    }

    /**
     * 获取当前用户属于什么vip
     * 1 最高级别vip
     * 2 公司员工 付费功能1分钱
     * @param int $uid 用户id
     * @param int $order_type
     * @param int $rid
     * @param int $gid 若有值则判断
     * @return bool|int 返回当前用户的VIP级别
     * @throws Exception
     */
    public static function getVips($uid, $order_type, $rid, $gid = 0)
    {
        $order_type = (int)$order_type;
        //定制报告和商场不支持vip
        if (in_array($order_type, [14, 15], true))
        {
            return 0;
        }

        //问答只有蹭看支持vip
        if ($order_type === 22)
        {
            if ($gid)
            {
                $question = (new Zlcr2UserQuestion())->getQuestionType((int)$gid);
                if ($question['type'] !== 1)
                {
                    return 0;
                }
            } else
            {
                return 0;
            }
        }

        $adminVipModel = new ZlAdminVip();
        $vip1 = $adminVipModel->getVipInfo(1);
        if (in_array($uid, $vip1, false))
        {
            return 1;
        }
        $vip2 = $adminVipModel->getVipInfo(2);
        if (in_array($uid, $vip2, false))
        {
            //不需要档案的商品
            if (in_array($order_type, [6, 8, 30, 31], true))
            {
                return 2;
            }
            // 获取当前用户的档案id
            $selfId = (new Recode())->getSelfRecode(['uid' => $uid]);
            if (isset($selfId['id']) && (int)$rid === (int)$selfId['id'])
            {
                return 2;
            }
        }
        return 0;
    }

    /**
     * 保证星座id在范围内   1~12
     * @param int $sign
     * @return int
     */
    public static function modSignId($sign = 0): int
    {
        return $sign < 1 ? 12 : ($sign > 12 ? 1 : $sign);
    }

    /**
     * 头像地址处理
     * 1、未带http/https的加上域名
     * 2、已有http/https的直接返回
     * @param string $img
     * @return string
     */
    public static function getImgUrl($img = ''): string
    {
        // 强制转换为string
        $img = (string)$img;
        if ($img && !preg_match('/^http(s?):\/\/*/', $img))
        {
            $img_host = Env::get('host.photo');
            $img = $img_host . $img;
        }
        return $img;
    }

    /**
     * 过去多久时间
     * @param int $dateTimeStamp 是一个时间毫秒，注意时间戳是秒的形式，在这个毫秒的基础上除以1000，就是十位数的时间戳。13位数的都是时间毫秒。
     * @param string $format 一年以上时，返回的时间格式 Y-m-d H:i:s.x
     * @return string
     */
    public static function timeago($dateTimeStamp = 0, $format = 'Y-m-d')
    {
        $minute = 1000 * 60; //把分，时，天，周，半个月，一个月用毫秒表示
        $hour = $minute * 60;
        $day = $hour * 24;
        $week = $day * 7;
        $month = $day * 30;
        $now = floor(microtime(true) * 1000); //获取当前时间毫秒
        $diffValue = $now - $dateTimeStamp;//时间差
        if ($diffValue < 0)
        {
            return '';
        }
        $minC = $diffValue / $minute; //计算时间差的分，时，天，周，月
        $hourC = $diffValue / $hour;
        $dayC = $diffValue / $day;
        $weekC = $diffValue / $week;
        $monthC = $diffValue / $month;
        if ($monthC >= 1 && $monthC <= 3)
        {
            $result = (int)($monthC) . '月前';
        } else if ($weekC >= 1 && $weekC <= 3)
        {
            $result = (int)($weekC) . '周前';
        } else if ($dayC >= 1 && $dayC <= 6)
        {
            $result = (int)($dayC) . '天前';
        } else if ($hourC >= 1 && $hourC <= 23)
        {
            $result = (int)($hourC) . '小时前';
        } else if ($minC >= 1 && $minC <= 59)
        {
            $result = (int)($minC) . '分钟前';
        } else if ($diffValue >= 0 && $diffValue <= $minute)
        {
            $result = '刚刚';
        } else
        {
            $result = date($format, $dateTimeStamp);
        }
        return $result;
    }

    /**
     * 时间戳格式转换 刚刚|一小时前
     * @param int $time 时间戳
     * @param string $format
     * @return string
     */
    public static function timeToStr(int $time, string $format = 'Y.m.d'): string
    {
        $dt = request()->time() - $time;
        if ($dt < 60)
        {
            $strTime = '刚刚';
        } elseif ($dt < 3600)
        {
            $strTime = ((int)($dt / 60)) . '分钟前';
        } elseif ($dt < 86400)
        {
            $strTime = ((int)($dt / 3600)) . '小时前';
        } elseif ($dt < 86400 * 7)
        {
            $strTime = ((int)($dt / 86400)) . '天前';
        } else
        {
            $strTime = date($format, $time);
        }

        return $strTime;
    }

    /**
     * 获取魔链地址
     * @param $type
     * @return mixed|string
     */
    public static function getMlink($type)
    {
        $mlinkHost = 'https://axelkv.mlinks.cc/';
        $data = [
            'astrolable' => $mlinkHost . 'Aene', // 准了跳转至星盘
            'composite' => $mlinkHost . 'AeTs', // 准了跳转至合盘
            'rectification' => $mlinkHost . 'AeTr',   //  准了跳转至校准生辰
            'predict' => $mlinkHost . 'AeTJ',   //  准了跳转至预测
            'divination' => $mlinkHost . 'AeTv',   //  准了跳转至提问
            'archives' => $mlinkHost . 'AeTR',   //  准了跳转至档案管理
            'article_conversations' => $mlinkHost . 'AeTw',   //  准了跳转至星座私房话
            'article_treasury' => $mlinkHost . 'AeT9',   //  准了跳转至开运宝典
            'article_details' => $mlinkHost . 'AeTx',   //  准了跳转至文章详情
            'rectification_details' => $mlinkHost . 'AeTP',   //  准了跳转至校准生辰结果
            'article_recommend' => $mlinkHost . 'AeTz',   //  准了跳转至星文推荐
            'week' => $mlinkHost . 'Aesp',   //  准了跳转至周运
            'month' => $mlinkHost . 'Aesq',   //  准了跳转至月运
            'year' => $mlinkHost . 'AesS',   //  准了跳转至年运
            'empty' => $mlinkHost . 'AesI',   //  准了APP启动--首页 默认页
            'fortune_for_month' => $mlinkHost . 'ABPb',   //  准了跳转至运势月运
            'fortune_for_year' => $mlinkHost . 'ABPe',   //  准了跳转至运势年运
            'acg_map' => $mlinkHost . 'ABPB',   //  准了跳转至地图首页
            'identify_love' => $mlinkHost . 'ABP1',   // 准了跳转至鉴爱首页
            'pay_orders_for_year' => $mlinkHost . 'ABPC',   //  准了跳转至年运支付结果页
            'course_list' => $mlinkHost . 'ABPg',   //  准了跳转至课程广场
            'my_course' => $mlinkHost . 'ABPZ',   //  准了跳转至我的课程页
            'course_detail' => $mlinkHost . 'ABPh',   // 准了跳转至课程详情页
            'report_index' => $mlinkHost . 'ABQr',   // 准了跳转至本命预测
            'report_details' => $mlinkHost . 'ABQ8',   // 准了跳转至本命预测详情

        ];

        return $data[$type] ?? $mlinkHost . 'AesI';
    }

    /**
     * 判断是否为时间戳
     * @param $time
     * @return bool
     */
    public static function is_strtotime($time)
    {
        try
        {
            $new = strtotime(date('Y-m-d H:i:s', $time));
        }
        catch (Exception $e)
        {
            $new = 0;
        }
        return $new == $time;
    }

    /**
     * 隐藏电话号码/手机号码中间4位
     * @param string $phone
     * @param int $area_code
     * @return string|string[]|null
     */
    public static function hidtel(string $phone, $area_code = 86)
    {
        // 隐藏邮箱
        if (strpos($phone, '@'))
        {
            $email_array = explode('@', $phone);
            $prevfix = (strlen($email_array[0]) < 4) ? '' : substr($phone, 0, 3); //邮箱前缀
            $count = 0;
            $str = preg_replace('/([\w+_-]{0,100})@/', '***@', $phone, -1, $count);
            return $prevfix . $str;
        }

        if ($area_code === 86)
        {
            // 隐藏联系方式中间4位
            $Istelephone = preg_match('/(0\d{2,3}[\-]?[2-9]\d{6,7}[\-]?\d?)/', $phone); //固定电话
            if ($Istelephone)
            {
                $res = preg_replace('/(0\d{2,3}[\-]?[2-9])\d{3,4}(\d{3}[\-]?\d?)/', '$1****$2', $phone);
            } else
            {
                $res = preg_replace('/(1\d{2})\d{4}(\d{4})/', '$1****$2', $phone);
            }
        } else
        {
            $res = substr_replace($phone, '****', 3, 4);
        }
        return $res;
    }

    /**
     * 发送钉钉消息 https://developers.dingtalk.com/document/app/custom-robot-access
     * @param $remote_server
     * @param $title
     * @param $text
     * @param int $type 1=>markdown,2=>link,3=>ActionCard
     * @param string $href_url
     * @return array|bool|string
     */
    public static function sendDingMsg($remote_server, $title, $text, $type = 1, $href_url = '')
    {
        switch ($type)
        {
            case 2:
                // link类型
                $data = [
                    'msgtype' => 'link',
                    'link' => [
                        'text' => $text,
                        'title' => $title,
                        'picUrl' => 'https://oss.goddessxzns.com/picture/uploads/logo.png?x-oss-process=style/show',
                        'messageUrl' => $href_url,
                    ],
                ];
//                if ($href_url)
//                {
//                    $data['link']['messageUrl'] = $href_url;
//                }
                break;
            case 3:
                //
                //        "text": "![screenshot](https://gw.alicdn.com/tfs/TB1ut3xxbsrBKNjSZFpXXcXhFXa-846-786.png)
                // ### 乔布斯 20 年前想打造的苹果咖啡厅
                // Apple Store 的设计正从原来满满的科技感走向生活化，而其生活化的走向其实可以追溯到 20 年前苹果一个建立咖啡馆的计划",
                $data = [
                    'msgtype' => 'actionCard',
                    'actionCard' => [
                        'title' => $title,
                        // markdown格式的消息
                        'text' => $text,
                        // 0-按钮竖直排列，1-按钮横向排列
                        'btnOrientation' => 0,
                    ],
                ];

                break;
            default:
                //markdown类型
                $data = [
                    'msgtype' => 'markdown',
                    'markdown' => [
                        'title' => $title,
                        'text' => $text,
                    ],
                ];
                break;

        }

        $post_string = json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);

        return self::curlRequest('POST', $remote_server, $post_string, ['Content-Type: application/json;charset=utf-8']);
    }

    /**
     * 二维数组追加元素
     * @param $data
     * @param $arr
     * @return array
     */
    public static function array_push_element($data, $arr)
    {
        array_walk($data, static function (&$value, $key, $arr)
        {
            $value = array_merge($value, $arr);
        }, $arr);
        //二维数组去重
        return array_unique($data, SORT_REGULAR);
    }

    /**
     * 多维数组转一维数组
     * @param $data
     * @return array
     */
    public static function array_many_one($data)
    {
        $result = [];
        array_walk_recursive($data, static function ($value) use (&$result)
        {
            $result[] = $value;
        });
        return $result;
    }

    /**
     * 创建文件
     * @param string $filename 文件名称 不带目录
     * @param string $content 文件内容
     * @param string $dir 文件所在目录
     */
    public static function createFile($filename = 'a.json', $content = '', $dir = 'tmp'): void
    {
        try
        {
            $fileDir = RUNTIME_PATH . $dir;

            !is_dir($fileDir) && mkdir($fileDir, 0755, true);

            $filePath = $fileDir . DS . $filename;

            file_put_contents($filePath, $content);
        }
        catch (Exception $e)
        {
            Log::write('createFile:' . $e->getMessage(), 'error');
        }
    }

    /**
     * 检测并重组运势时间格式,获取运势购买的时间范围
     * @param int $fortune_type 2=>日运，3=》月运，4=》年运
     * @param string $start_time
     * @param int $gid
     * @return array
     * @throws ApiException
     * @throws Exception
     */
    public static function FormatFortuneTime($fortune_type, $start_time, $gid = 0): array
    {
        $test_time = explode('-', $start_time);
        switch ($fortune_type)
        {
            case 2:
                try
                {
                    [$year, $month, $day] = $test_time;
                }
                catch (Exception $e)
                {
                    throw new ApiException('时间不符合格式,请重试');
                }
                if (count($test_time) !== 3 && (strlen($year) !== 4 || !in_array(strlen($month), [1, 2], true) || !in_array(strlen($day), [1, 2], true)))
                {
                    throw new ApiException('时间不符合格式,请重试');
                }
                $start_time_arr = $year . '-' . $month . '-' . $day;
                $start = '2018-01-01';
                $end = '2050-01-01';
                break;
            case 3:
            case 12:
                try
                {
                    [$year, $month] = $test_time;
                }
                catch (Exception $e)
                {
                    throw new ApiException('时间不符合格式,请重试');
                }
                if (count($test_time) !== 2 && (strlen($year) !== 4 || !in_array(strlen($month), [1, 2], true)))
                {
                    throw new ApiException('时间不符合格式,请重试');
                }
                $start_time_arr = $year . '-' . $month . '-1';
                $fortuneTimeRangeConfig = config('fortune.fortune_time_range');
                $start = $fortuneTimeRangeConfig['month']['start'] . '-1 00:00:00';
                $end = $fortuneTimeRangeConfig['month']['end'] . '-31 23:59:59';
                break;
            case  4:
            case  27:
                try
                {
                    [$year] = $test_time;
                }
                catch (Exception $e)
                {
                    throw new ApiException('时间不符合格式,请重试');
                }
                if (strlen($year) !== 4)
                {
                    throw new ApiException('时间不符合格式,请重试');
                }
                $start_time_arr = $year . '-1-1';
                $fortuneTimeRangeConfig = config('fortune.fortune_time_range');
                $start = $fortuneTimeRangeConfig['year']['start'] . '-1-1 00:00:00';
                $end = $fortuneTimeRangeConfig['year']['end'] . '-12-31 23:59:59';
                break;
            case 17:
                try
                {
                    [$year, $month] = $test_time;
                }
                catch (Exception $e)
                {
                    throw new ApiException('时间不符合格式,请重试');
                }
                if (count($test_time) !== 2 && (strlen($year) !== 4 || !in_array(strlen($month), [1, 2], true)))
                {
                    throw new ApiException('时间不符合格式,请重试');
                }
                $start_time_arr = $year . '-' . $month . '-01';
                $start = date('Y-m-01');
                $end = '2058-01-01';
                break;
            default:
                throw new ApiException('时间不符合格式,请重试');
        }
        $fortune_start_time = strtotimeModify('U', $start);
        $fortune_end_time = strtotimeModify('U', $end);
        $start_time_int = strtotimeModify('U', $start_time_arr);//获取时间戳
        $end_time = 0;

        if ($gid)
        {
            $goods = (new Goods)->getInfoById($gid);
            $end_time = strtotimeModify('U', $start_time, $goods['expire_time']) - 86400;
            //购买时间超出2018-2037提示用户不可购买
            if (isset($fortune_start_time, $fortune_end_time, $start_time_int, $end_time) && ($start_time_int < $fortune_start_time || $end_time > $fortune_end_time))
            {
                throw new ApiException('您购买的运势时间超出时间范围，请重新确认。');
            }
        }
        return ['start_time' => $start_time_int, 'end_time' => $end_time, 'start_date' => $start_time_arr];
    }

    // /**
    //  * 发送消息到队列
    //  * @param string $queueName 队列名称 不带_task
    //  * @param array $jobData
    //  * @param int|null $delay 自定义 延期时间
    //  * @throws ApiException
    //  */
    // public static function queuePush(string $queueName, $jobData, $delay = null): void
    // {
    //     $queueConfig = config('queue.queues');
    //     if (isset($queueConfig[$queueName]['task']))
    //     {   // 新队列模式    统一队列名
    //         $task = $queueConfig[$queueName]['task'] ?? '';
    //         $queue = $queueConfig[$queueName]['queue'] ?? $queueConfig['default_queue'];
    //         $delay = $delay ?? ($queueConfig[$queueName]['delay'] ?? $queueConfig['default_delay']);
    //     } else
    //     {   // 旧队列模式
    //         $task = $queueConfig[$queueName . '_task'] ?? '';
    //         $queue = $queueConfig[$queueName . '_queue'] ?? '';
    //         $delay = $delay ?? ($queueConfig[$queueName . '_delay'] ?? $queueConfig['default_delay']);
    //     }

    //     // if ($task && $queue)
    //     // {   // 添加到队列
    //     //     $isUseRabbitmq = 0;//是否使用Rabbitmq，1：使用；0不使用；
    //     //     if ($isUseRabbitmq)
    //     //     {
    //     //         require_once __DIR__ . '/common/lib/RabbitmqRectificationProducer.php';

    //     //         RabbitmqRectificationProducer::sendMessage($delay, $jobData);
    //     //     }// 使用think-queue+redis的方式实现消息队列
    //     //     else
    //     //     {
    //             if ($delay <= 0)
    //             {
    //                 try
    //                 {
    //                     Queue::push($task, $jobData, $queue);// 立即执行
    //                 }
    //                 catch (Exception $e)
    //                 {
    //                     // 加入立即执行队列失败
    //                     Log::write('加入立即执行队列失败:' . var_export($jobData, true), 'error', true);
    //                 }
    //             } else
    //             {
    //                 try
    //                 {
    //                     Queue::later($delay, $task, $jobData, $queue);// 延迟15分钟执行
    //                 }
    //                 catch (Exception $e)
    //                 {
    //                     // 加入立即延时队列失败
    //                     Log::write('加入立即延时队列失败:' . var_export($jobData, true), 'error', true);
    //                 }
    //             }
    //       // }
    //   // } else
    //     {
    //         throw new ApiException($queueName . '队列配置缺失', 200, config('code.error'));
    //     }
    // }

     /**
     * 添加任务到队列
     *
     * @param string $queueName 队列名称
     * @param array $jobData 数据
     * @param int|null $delay 延迟时间（秒）
     * @throws \Exception
     */
    public static function queuePush(string $queueName, array $jobData, int $delay = null): void
    {
        $queueConfig = config("queue.queues.$queueName");

        if (!$queueConfig || !isset($queueConfig['task'], $queueConfig['queue'], $queueConfig['delay'])) {
            throw new \Exception("队列配置不完整：$queueName");
        }

        $taskClass = $queueConfig['task'];
        $queue = $queueConfig['queue'];
        $delay = $delay ?? $queueConfig['delay'];

        try {
            if ($delay <= 0) {
                Bus::dispatch(new $taskClass($jobData))->onQueue($queue);
            } else {
                Bus::dispatch(new $taskClass($jobData))->delay($delay)->onQueue($queue);
            }
        } catch (\Exception $e) {
            Log::channel('queue')->error("任务推送失败 [$queueName]: " . $e->getMessage(), [
                'queue' => $queue,
                'jobData' => $jobData,
                'taskClass' => $taskClass,
            ]);
        }
    }


    /**
     * 微信头像上传OSS
     * @param string $url
     * @param string $path 图片上传路径
     * @return array
     * @throws GuzzleException
     */
    public static function wxImgUploadOss(string $url, string $path = ''): array
    {
        try
        {
            $client = new Client();
            // 获取图片信息
            $request = $client->request('GET', $url);
        }
        catch (Exception $e)
        {
            Log::write('微信头像信息获取失败:' . $e->getMessage(), 'error', true);
            $errorMessage = $e->getMessage();
            return [
                'success' => false,
                'url' => '',
                'message' => $errorMessage,
            ];
        }

        $httpCode = $request->getStatusCode();
        if ($httpCode === 200)
        {   // 判断头像 地址有效 并存入OSS
            $file = $request->getBody()->getContents();
            // 生成随机文件地址和文件名
            $fileName = $path . DS . date('Ymd') . DS . md5(microtime(true)) . '.png';

            try
            {
                AliOss::getInstance()->ossUpload($fileName, $file);
            }
            catch (OssException $e)
            {
                Log::write('微信头像上传失败:' . $e->getMessage(), 'error', true);
                return [
                    'success' => false,
                    'url' => '',
                    'message' => $e->getMessage(),
                ];
            }

            return [
                'success' => true,
                'url' => DS . $fileName,
                'message' => '',
            ];
        }

        return [
            'success' => false,
            'url' => '',
            'message' => '图片文件失效',
        ];
    }

    /**
     * curl get请求
     * @param string $url
     * @return bool|string
     */
    public static function curl_get($url = '')
    {
        try
        {
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_HEADER, 0);
            $output = curl_exec($ch);
            curl_close($ch);
            return $output;
        }
        catch (Exception $e)
        {
            return false;
        }
    }


    /**
     * curl请求
     * @param string $requestType DELETE PUT PATCH POST GET
     * @param $url
     * @param $data
     * @param array $header
     * @return mixed
     */
    public static function curlRequest($requestType = 'GET', $url = '', $data = [], $header = [])
    {
        $curl = curl_init();
        //强制获取一个新的连接，替代缓存中的连接。
        curl_setopt($curl, CURLOPT_FRESH_CONNECT, true);
        //返回原生的数据
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        //如不输出json, 请打开这行代码，启用时会将头文件的信息作为数据流输出。
        curl_setopt($curl, CURLOPT_HEADER, false);
        //请求地址
        curl_setopt($curl, CURLOPT_URL, $url);
        //请求头
        if (!empty($header))
        {
            curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
        }
        if ($requestType === 'POST')
        {
            //发送post请求
            curl_setopt($curl, CURLOPT_POST, 1);
        } elseif (in_array($requestType, ['PUT', 'PATCH', 'DELETE']))
        {
            //自定义请求类型，需要服务器支持
            curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $requestType);
        }
        if ($data)
        {
            //发送请求数据 通过header控制，数据可以是json或数组
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        }
        //显示HTTP状态码，默认行为是忽略编号小于等于400的HTTP信息。
        curl_setopt($curl, CURLOPT_FAILONERROR, false);
        if (1 == strpos("$" . $url, "https://"))
        {
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
        }
        $output = curl_exec($curl);
        // 检查是否有错误发生
        if (curl_errno($curl))
        {
            $output = curl_error($curl);
        }
        curl_close($curl);
        return $output;
    }

    /**
     * 检测手机号格式，不可注册
     * @param string $tel
     * @param int $areaCode
     * @param string $msg
     * @return array
     * @throws ApiException
     */
    public static function CheckUserTel(string $tel, int $areaCode = 86, string $msg = '手机号错误'): array
    {
        // 手机号是否存在
        if (!Util::telRuleCheck($tel, $areaCode))
        {
            Log::write($areaCode . $tel . $msg, 'error');
            throw new ApiException($msg, 200, 0);
        }
        //判断手机号是否注册过
        try
        {
            $tel = $areaCode === 86 ? $tel : $areaCode . '-' . $tel;

            $user = (new User())->field('id,forbidden,has_self_recode,password,nickname,uc_id,young_pwd,avatar,token_expire,token,uen,is_login_record')->where(['tel' => (string)$tel])->findOrFail()->toArray();
        }
        catch (\think\Exception $e)
        {
            $user = [];
        }
        return $user;
    }

    /**
     * 获取当前ECS的ip地址 或 hostname
     * 主要用于队列
     * @return string
     */
    public static function getEcsIp()
    {
        request()->header();
        try
        {
            // centos获取系统ip
            exec('/sbin/ifconfig eth0 | sed -n \'s/^ *inet \\([0-9.]\\{7,\\}\\) .*$/\\1/p\'', $arr1);

            exec('hostname', $arr2);

            $host = $arr1[0] ?? ($arr2[0] ?? '');
        }
        catch (Exception $e)
        {
            $host = '';
        }
        return $host;
    }

    /**
     * 手机号正则验证
     * @param string $tel
     * @param int $areaCode
     * @return bool
     */
    public static function telRuleCheck(string $tel, int $areaCode = 86)
    {
        $areaCodeAll = [
            86 => 'CN',
            852 => 'HK',
            853 => 'l6',
            886 => 'TW',
            93 => 'l6',
            355 => 'l6',
            213 => 'DZ',
            376 => 'l6',
            244 => 'l6',
            1264 => 'l6',
            1268 => 'l6',
            54 => 'default',//l6
            297 => 'default',
            61 => 'AU',
            43 => 'l6',
            994 => 'l6',
            20 => 'l6',
            372 => 'l6',
            251 => 'l6',
            353 => 'default',// l6
            968 => 'l6',
            971 => 'default',// l6
            1242 => 'l6',
            973 => 'l6',
            1246 => 'l6',
            375 => 'l6',
            32 => 'BE',
            501 => 'l6',
            229 => 'l6',
            1441 => 'l6',
            975 => 'default',
            591 => 'l6',
            387 => 'default',
            267 => 'l6',
            55 => 'BR',
            359 => 'l6',
            226 => 'l6',
            257 => 'l6',
            354 => 'l6',
            92 => 'l6',
            970 => 'default',
            507 => 'l6',
            675 => 'l6',
            595 => 'l6',
            48 => 'PL',
            1787 => 'l6',
            240 => 'default',
            45 => 'l8',
            1767 => 'default',
            1809 => 'l6',
            49 => 'DE',
            670 => 'default',
            228 => 'l6',
            593 => 'l6',
            291 => 'default',
            7 => 'RU',
            298 => 'default',
            679 => 'l6',
            358 => 'default',
            33 => 'FR',
            594 => 'l6',
            689 => 'l6',
            63 => 'PH',
            57 => 'l6',
            506 => 'l6',
            53 => 'l6',
            243 => 'l6',
            220 => 'l6',
            995 => 'l6',
            299 => 'default',
            1473 => 'default',
            590 => 'default',
            1671 => 'l6',
            502 => 'l6',
            592 => 'l6',
            242 => 'l6',
            509 => 'l6',
            504 => 'l6',
            382 => 'default',
            31 => 'default',// l6
            82 => 'default',// l6   default=>韩国手机号支持01086658166
            855 => 'l6',
            1 => 'default',
            420 => 'CZ',
            253 => 'l6',
            241 => 'l6',
            233 => 'l6',
            224 => 'l6',
            245 => 'default',
            686 => 'default',
            996 => 'default',
            263 => 'l6',
            237 => 'l6',
            238 => 'default',
            1345 => 'l6',
            269 => 'default',
            682 => 'l6',
            385 => 'default',
            599 => 'l6',
            254 => 'l6',
            965 => 'l6',
            974 => 'default',// l6
            856 => 'l6',
            371 => 'l6',
            961 => 'l6',
            266 => 'l6',
            231 => 'l6',
            218 => 'l6',
            423 => 'l6',
            370 => 'l6',
            352 => 'l6',
            262 => 'l6',
            40 => 'l6',
            250 => 'default',
            1684 => 'default',
            880 => 'l6',
            389 => 'default',
            261 => 'l6',
            265 => 'l6',
            60 => 'default',// MY
            960 => 'l6',
            223 => 'l6',
            356 => 'l6',
            596 => 'l6',
            222 => 'default',
            230 => 'l6',
            52 => 'l6',
            373 => 'l6',
            377 => 'l6',
            976 => 'l6',
            1664 => 'l6',
            212 => 'l6',
            258 => 'l6',
            95 => 'default',// l6
            51 => 'l6',
            1284 => 'default',
            264 => 'l6',
            977 => 'l6',
            505 => 'l6',
            227 => 'l6',
            234 => 'l6',
            47 => 'NO',
            27 => 'l9',
            680 => 'default',
            351 => 'PT',
            81 => 'JP',
            46 => 'default',// l6
            41 => 'l6',
            357 => 'l6',
            503 => 'l6',
            1869 => 'default',
            1758 => 'l6',
            508 => 'default',
            1784 => 'l6',
            685 => 'l6',
            378 => 'l6',
            239 => 'l6',
            966 => 'SA',
            221 => 'l6',
            381 => 'l6',
            248 => 'l6',
            232 => 'l6',
            1721 => 'default',
            421 => 'l6',
            386 => 'l6',
            677 => 'l6',
            252 => 'l6',
            94 => 'l6',
            249 => 'l6',
            597 => 'l6',
            268 => 'l6',
            992 => 'l6',
            255 => 'l6',
            66 => 'default',// l6
            676 => 'l6',
            1868 => 'default',
            216 => 'l6',
            90 => 'TR',
            993 => 'l6',
            1649 => 'default',
            673 => 'l6',
            256 => 'l6',
            380 => 'l6',
            598 => 'l6',
            998 => 'default',
            678 => 'default',
            58 => 'l6',
            30 => 'GR',
            36 => 'HU',
            225 => 'l6',
            687 => 'default',
            64 => 'NZ',
            65 => 'default',// l6
            34 => 'ES',
            963 => 'SY',
            374 => 'l6',
            91 => 'IN',
            62 => 'l6',
            98 => 'l6',
            964 => 'l6',
            972 => 'IL',
            39 => 'IT',
            1876 => 'l6',
            962 => 'default',
            44 => 'default',// l6
            84 => 'VN',
            1340 => 'default',
            967 => 'l6',
            236 => 'l6',
            235 => 'l6',
            56 => 'l6',
            350 => 'l6',
            260 => 'default',
        ];
        if (isset($areaCodeAll[$areaCode]))
        {
            $urle = [
                // 中国
                'CN' => "/^1[3456789]\d{9}$/",
                // 台湾
                'TW' => "/^(0)?9\d{8}$/",
                // 香港
                'HK' => "/^[5|6|9]\d{7}$/",
                // 菲律宾 63
                'PH' => "/^\d{10}$/",
                // 阿尔及利亚
                'DZ' => "/^(5|6|7)\d{8}$/",
                // 叙利亚 963
                'SY' => "/^(0)?9\d{8}$/",
                // 沙特阿拉伯 966
                'SA' => "/^5\d{8}$/",
                // 捷克 420
                'CZ' => "/^[1-9][0-9]{2}\d{6}$/",
                // 德国 49
                'DE' => "/^\d{10,11}$/",
                // 希腊 30
                'GR' => "/^(69\d{8})$/",
                // 土耳其 90
                'TR' => "/^5\d{9}$/",
                // 澳大利亚
                'AU' => "/^4\d{8}$/",
                // 印度 91
                'IN' => "/^(0)?[789]\d{9}$/",
                // 新西兰 64
                'NZ' => "/^(0)?2\d{7,9}$/",
                // 赞比亚 260
                'ZM' => "/^(26)?09[567]\d{7}$/",
                // 西班牙 34
                'ES' => "/^(6\d{1}|7[1234])\d{7}$/",
                // 法国 33
                'FR' => "/^(0)?[67]\d{8}$/",
                // 以色列 972
                'IL' => "/^(0)?([23489]|5[0248]|77)[1-9]\d{6}/",
                // 匈牙利 36
                'HU' => "/^(20|30|70)\d{7}$/",
                // 意大利 39
                'IT' => "/^3\d{2}\d{6,7}$/",
                // 日本 81
                'JP' => "/^\d{1,4}\d{1,4}\d{4}$/",
                // 挪威 40
                'NO' => "/^[49]\d{7}$/",
                // 比利时 32
                'BE' => "/^4?\d{8}$/",
                // 波兰 48
                'PL' => "/^[5-8]\d{8}$/",
                // 巴西 55
                'BR' => "/^[1-9]{2}\-?[2-9]{1}\d{3,4}\-?\d{4}$/",
                // 葡萄牙 351
                'PT' => "/^9[1236]\d{7}$/",
                // 俄罗斯 7
                'RU' => "/^9\d{9}$/",
                // 马来西亚 60
                'MY' => "/^(\+?6?01){1}(([145]{1}(\-|\s)?\d{7,8})|([236789]{1}(\s|\-)?\d{7}))$/",
                // 越南 84
                'VN' => "/^(84|0)?((1(2([0-9])|6([2-9])|88|99))|(9((?!5)[0-9])))([0-9]{7})$/",
                'l6' => "/^\d{6}$/",
                'l7' => "/^\d{7}$/",
                'l8' => "/^\d{8}$/",
                'l9' => "/^\d{9}$/",
                'default' => "/^\d{4,11}$/",
            ];

            if (preg_match($urle[$areaCodeAll[$areaCode]], $tel))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取版本号int
     * @param $version
     * @return mixed
     */
    public static function getVersionInt($version = '')
    {
        $version = $version ?: request()->header('version', '1.0.0');
        $globalKey = 'global_version_int_' . $version;
        try
        {
            return $GLOBALS[$globalKey];
        }
        catch (\Exception $e)
        {
            $c = explode('.', $version);
            $v = (int)(sprintf("%02d%02d%02d", $c[0], $c[1], $c[2]));
            $GLOBALS[$globalKey] = $v;
            return $GLOBALS[$globalKey];
        }
    }

    /**
     * 判断版本号大小关系
     * @param string $requestedVersion 请求的版本号
     * @param string $currentVersion 当前的版本号
     * @return string 大 || 小 || 同
     */
    public static function checkAppVersion(string $requestedVersion, string $currentVersion = '')
    {
        $currentVersion = $currentVersion ?: request()->header('version', '1.0.0');
        // 使用version_compare进行版本号比较，简化逻辑
        $result = version_compare($currentVersion, $requestedVersion);

        if ($result > 0)
        {
            return '大';
        }
        if ($result < 0)
        {
            return '小';
        }
        return '同';
    }

    /**
     * 判断安卓是否大于此版本
     * @param $version
     * @return bool true=>大于等于，false=>小于
     */
    public static function androidVersion($version)
    {
        $app_type = (string)(request()->header()['app-type'] ?? '');

        if ($app_type === 'android')
        {
            $res = self::CheckAppVersion($version);
            if ($res !== '小')
            {
                return true;
            }
        }
        return false;
    }


    /**
     * 重复评论验证
     * @param $uid
     * @param array $data 要验证的数据
     * @return bool true 5秒内重复评论 false 非重复评论|允许时间间隔评论
     * @throws Exception
     */
    public static function commentCheck($uid, $data = [], $expireTime = -1)
    {
        $hashStr = md5(implode(',', $data));
        $cache = CacheManage::getInstance()->setCacheConfig('content.reuse_comment_ckeck', $uid . ':' . $hashStr);
        if ($expireTime <= -1)
        {
            $expireTime = $cache->cacheKey['reuse_comment_time'] ?? 0;
        }
        $redis = $cache->redisHandler;

        $num = $redis->incr($cache->cacheKey['details']);
        if ($num === 1)
        {
            $redis->expire($cache->cacheKey['details'], $expireTime);
            return false;
        } else
        {
            return true;
        }
    }

    /**
     * 实时分析通用写日志方法
     * @param int $type 1：新用户注册日志；2：新增档案日志；3：支付回调日志；4：准币支付回调或者准币消费日志；
     * @param $arr
     */
    public static function logRealTimeAnalyzeWrite(int $type, $arr)
    {
        //日志先不写了，因为MaxCompute的效果不错，污染业务代码
//        try
//        {
//            //原始的日志格式
//            $logs = [
//                'log_type' => $type,
//                'user_uid' => '',
//                'user_name' => '',
//                'user_sex' => '',
//                'user_reg_type' => '',
//                'user_ip' => '',
//                'recode_uid' => '',
//                'recode_rid' => '',
//                'recode_sex' => '',
//                'recode_name' => '',
//                'recode_isself' => '',
//                'recode_year' => '',
//                'recode_month' => '',
//                'recode_day' => '',
//                'recode_hour' => '',
//                'recode_minute' => '',
//                'recode_timezone' => '',
//                'recode_birth_country' => '',
//                'recode_birth_province' => '',
//                'recode_birth_city' => '',
//                'recode_live_country' => '',
//                'recode_live_province' => '',
//                'recode_live_city' => '',
//                'recode_sun' => '',
//                'recode_ip' => '',
//                'pay_notify_uid' => '',
//                'pay_notify_order_id' => '',
//                'pay_notify_order_type' => '',
//                'pay_notify_total_amount' => '',
//                'pay_notify_gid' => '',
//                'coin_pay_uid' => '',
//                'coin_pay_order_id' => '',
//                'coin_pay_order_type' => '',
//                'coin_pay_total_amount' => '',
//                'coin_pay_gid' => '',
//                'coin_pay_consumer_type' => ''
//            ];
//            $logs = array_merge($logs, $arr);
//
//            Log::write($logs, 'flog_realtime_analyze');
//        }
//        catch (Exception $e)
//        {
//            Log::write($type . '类型写日志失败：' . $e->getMessage(), 'flog_realtime_analyze_log_error', true);
//        }
    }

    /**
     * 直播聊天写日志方法（注意：zhunle_api Event类里写到了*_flog_live_chat_log_cli.log里）【阿里云日志系统支持多个路径采集】
     * @param $arr
     */
    public static function logWriteOfLiveChatMsg($arr): void
    {
        try
        {
            //原始的日志格式
            $logs = [
                'who_type' => '',//发送类型
                'who_id' => '',//直播间id 或者 用户id 或者 clientid
                'who_uid' => '',//发送消息的用户id
                'msg_action' => '',//发送的消息类型
                'msg_content' => '',//发送的消息内容（不能为空，string）]);
            ];
            $logs = array_merge($logs, $arr);

            $result = Log::write($logs, 'flog_live_chat_log');
            if (!$result)
            {
                Log::write('直播聊天写日志方法-写日志失败：' . json_encode($arr, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), 'error', true);
            }
        }
        catch (Exception $e)
        {
            Log::write('直播聊天写日志方法-写日志失败：' . $e->getMessage(), 'flog_live_chat_log_error', true);
        }
    }

    /**
     * 获取当前渠道ID
     * @return int
     */
    public static function getChannelId(): int
    {
        $header = request()->header();
        $appType = $header['app-type'] ?? '';
        if ($appType === 'ios')
        {
            $channel = $header['channel'] ?? 4;
        } elseif ($appType === 'android')
        {
            $configChannel = [
                5 => 'douyin',
                6 => '91com',
                7 => '360cn',
                8 => 'android',
                9 => 'anzhi',
                10 => 'baidu',
                11 => 'huawei',
                12 => 'jifeng',
                13 => 'jinli',
                14 => 'meizu',
                15 => 'mumayi',
                16 => 'OPPO',
                17 => 'sanxing',
                18 => 'smartisan',
                19 => 'sougou',
                20 => 'tecent',
                21 => 'VIVO',
                22 => 'wandoujia',
                23 => 'xiaomi',
                24 => 'xinlang',
                25 => 'zhunle',
                26 => 'google',
                84 => 'honor',
            ];

            $channel = array_search($header['channel'] ?? '', $configChannel, true) ?: 0;
        } else
        {
            $channel = $header['channel'] ?? '';
            if (!$channel)
            {
                switch ($appType)
                {
                    case 'WEB':
                        $channel = 1;
                        break;
                    case 'WEAPP':
                        $channel = 2;
                        break;
                    case 'TT':
                        $channel = 3;
                        break;
                    default:
                        $channel = 0;
                }
            }
        }

        return (int)$channel;
    }

    /**
     * 计算两个日期之间相差的时间
     * @param $fromTime
     * @param $toTime
     * @return array
     */
    public function datediff($fromTime, $toTime)
    {
        //转时间戳
        $begin_time = strtotime($fromTime);
        $end_time = strtotime($toTime);
        return $this->timediff($begin_time, $end_time);
    }

    /**
     * PHP 计算两个时间戳之间相差的时间
     * 功能：计算两个时间戳之间相差的日时分秒
     * @param int $begin_time 开始时间戳
     * @param int $end_time 结束时间戳
     * @return array
     */
    public function timediff($begin_time, $end_time)
    {
        if ($begin_time < $end_time)
        {
            $starttime = $begin_time;
            $endtime = $end_time;
        } else
        {
            $starttime = $end_time;
            $endtime = $begin_time;
        }
        //计算天数
        $timediff = $endtime - $starttime;
        $days = (int)($timediff / 86400);
        //计算小时数
        $remain = $timediff % 86400;
        $hours = (int)($remain / 3600);
        //计算分钟数
        $remain %= 3600;
        $mins = (int)($remain / 60);
        //计算秒数
        $secs = $remain % 60;
        return ['day' => $days, 'hour' => $hours, 'min' => $mins, 'sec' => $secs];
    }

    /**
     * 检测当前是否为非法请求
     * @param bool $write_log 是否写入日志
     * @return bool true 是｜false 正常请求
     */
    public static function isIllegalRequest($write_log = false)
    {
        $headerTime = request()->header('time');// string
        $headerDid = request()->header('did');// string

        $illegalTimes = ['20200115145358'];
        $illegalDid = ['ffffffff-b22b-b531-ffff-fffff9ef3f8d'];

        if (in_array($headerTime, $illegalTimes, true) || in_array($headerDid, $illegalDid, true))
        {
            $headerInfo = request()->header();
            $write_log = false;// 强制关闭日志写入
            if ($write_log)
            {
//                Log::write('非法获取验证码：' . json_encode($headerInfo, true), 'log', true);
                Log::write('非法获取验证码：' . request()->url(), 'log', true);
            }
            return true;
        }
        return false;
    }

    /**
     * 获取本周所有日期范围
     * @param string $time
     * @param string $format
     * @return array
     */
    public static function getWeekDateALL($time = '', $format = 'Y-m-d')
    {
        $time = $time != '' ? $time : time();
        //获取当前周几
        $week = date('w', $time);
        $week = $week < 1 ? 7 : $week;
        $date = [];
        for ($i = 1; $i <= 7; $i++)
        {
            $date[$i] = date($format, strtotime('+' . ($i - $week) . ' days', $time));
        }
        return $date;
    }

    /*
    * 获取当月有多少天
    * @param string date 例如：2021/02或2021/2
    * return int
    */
    public static function getMonthDays($date)
    {
        $tem = explode('/', $date);       //切割日期  得到年份和月份
        $year = $tem['0'];
        $month = (int)$tem['1'];
        if (in_array($month, [1, 3, 5, 7, 8, 10, 12], true))
        {
            $days = 31;
        } elseif ($month == 2)
        {
            if ($year % 400 == 0 || ($year % 4 == 0 && $year % 100 !== 0))        //判断是否是闰年
            {
                $days = 29;
            } else
            {
                $days = 28;
            }

        } else
        {
            $days = 30;
        }
        return $days;
    }

    /**
     * 快递单号物流状态 查询
     * https://market.aliyun.com/products/56928004/cmapi021863.html?spm=5176.730005.productlist.d_cmapi021863.23143524nAuFdp&innerSource=search_%E5%BF%AB%E9%80%92%E7%8A%B6%E6%80%81%E6%9F%A5%E8%AF%A2#sku=yuncode1586300000
     * @param string $no
     * @param string $type (可选)快递公司字母简写：不知道可不填 95%能自动识别，填写查询速度会更快【见产品详情】
     * @return mixed
     */
    public static function getKdiInfo($no = '', $type = '')
    {
        $host = "https://wuliu.market.alicloudapi.com";//api访问链接
        $path = "/kdi";//API访问后缀
        $method = "GET";
        $appcode = "7dd1391c76eb403299b4afba18aabe59";//替换成自己的阿里云appcode
        $headers = [];
        $headers[] = "Authorization:APPCODE " . $appcode;
        $querys = "no={$no}&type={$type}";  //参数写在这里
        $url = $host . $path . "?" . $querys;//url拼接
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($curl, CURLOPT_FAILONERROR, false);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_HEADER, false);
        //curl_setopt($curl, CURLOPT_HEADER, true); 如不输出json, 请打开这行代码，打印调试头部状态码。
        //状态码: 200 正常；400 URL无效；401 appCode错误； 403 次数用完； 500 API网管错误
        if (1 == strpos("$" . $host, "https://"))
        {
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
        }
        /* status 0:正常查询 201:快递单号错误 203:快递公司不存在 204:快递公司识别失败 205:没有信息 207:该单号被限制，错误单号 */
        // {
        //     "status": "0",/* status 0:正常查询 201:快递单号错误 203:快递公司不存在 204:快递公司识别失败 205:没有信息 207:该单号被限制，错误单号 */
        //     "msg": "ok",
        //     "result": {
        //         "number": "780098068058",
        //         "type": "zto",
        //         "list": [{
        //                 "time": "2018-03-09 11:59:26",
        //             "status": "【石家庄市】快件已在【长安三部】 签收,签收人: 本人,感谢使用中通快递,期待再次为您服务!"
        //         }, {
        //                 "time": "2018-03-09 09:03:10",
        //             "status": "【石家庄市】 快件已到达 【长安三部】（0311-85344265）,业务员 容晓光（13081105270） 正在第1次派件, 请保持电话畅通,并耐心等待"
        //         }, {
        //                 "time": "2018-03-08 23:43:44",
        //             "status": "【石家庄市】 快件离开 【石家庄】 发往 【长安三部】"
        //         }, {
        //                 "time": "2018-03-08 21:00:44",
        //             "status": "【石家庄市】 快件到达 【石家庄】"
        //         }, {
        //                 "time": "2018-03-07 01:38:45",
        //             "status": "【广州市】 快件离开 【广州中心】 发往 【石家庄】"
        //         }, {
        //                 "time": "2018-03-07 01:36:53",
        //             "status": "【广州市】 快件到达 【广州中心】"
        //         }, {
        //                 "time": "2018-03-07 00:40:57",
        //             "status": "【广州市】 快件离开 【广州花都】 发往 【石家庄中转】"
        //         }, {
        //                 "time": "2018-03-07 00:01:55",
        //             "status": "【广州市】 【广州花都】（020-37738523） 的 马溪 （18998345739） 已揽收"
        //         }],
        //         "deliverystatus": "3", /* 0：快递收件(揽件)1.在途中 2.正在派件 3.已签收 4.派送失败 5.疑难件 6.退件签收  */
        //         "issign": "1",                      /*  1.是否签收                  */
        //         "expName": "中通快递",              /*  快递公司名称                */
        //         "expSite": "www.zto.com",           /*  快递公司官网                */
        //         "expPhone": "95311",                /*  快递公司电话                */
        //         "courier": "容晓光",                /*  快递员 或 快递站(没有则为空)*/
        //                 "courierPhone":"13081105270",       /*  快递员电话 (没有则为空)     */
        //                 "updateTime":"2019-08-27 13:56:19", /*  快递轨迹信息最新时间        */
        //                 "takeTime":"2天20小时14分",         /*  发货到收货消耗时长 (截止最新轨迹)  */
        //                 "logo":"https://img3.fegine.com/express/zto.jpg" /* 快递公司LOGO */
        //     }
        // }
        return json_decode(curl_exec($curl), true);
    }

    /**
     * 数字转化成对应的汉字 mun2ch( 1 );
     * @param string $num
     * @return string
     */
    public function numberChinese($num) //整体读取转换
    {
        $num_real = $this->delNumFrontZero($num);//去掉前面的“0”
        $numlen = strlen($num_real);
        $a = "";
        $b = "";
        $c = "";
        if ($numlen >= 9)//如果满九位，读取“亿”位
        {
            $y = $num_real[strlen($num_real) - 9];
            $wsbq = substr($num_real, -8, 4);
            $gsbq = substr($num_real, -4);
            $a = $this->replaceNum($this->delNumFrontZero($gsbq));
            $b = $this->replaceNum($this->delNumFrontZero($wsbq)) . "万";
            $c = $this->replaceNum($this->delNumFrontZero($y)) . "亿";
        } elseif ($numlen <= 8 && $numlen >= 5) //如果大于等于“万”
        {
            $wsbq = substr($num_real, 0, $numlen - 4);
            $gsbq = substr($num_real, -4);
            $a = $this->replaceNum($this->delNumFrontZero($gsbq));
            $b = $this->replaceNum($this->delNumFrontZero($wsbq)) . "万";
            $c = "";
        } elseif ($numlen <= 4) //如果小于等于“千”
        {
            $gsbq = substr($num_real, -$numlen);
            $a = $this->replaceNum($this->delNumFrontZero($gsbq));
            $b = "";
            $c = "";
        }
        return $c . $b . $a;
    }

    /**
     * 去掉数字段前面的0
     * @param $num
     * @return string
     */
    public function delNumFrontZero($num)
    {
        return "" . (int)$num;
    }

    /**
     * 读取数值（4位）
     * @param $abcd
     * @return string
     */
    public function replaceNum($abcd)
    {
        $arr = [];
        $str = ""; //读取后的汉字数值
        $flag = 0; //该位是否为零
        $flag_end = 1; //是否以“零”结尾
        $size_r = strlen($abcd);
        for ($i = 0; $i < $size_r; $i++)
        {
            $arr[$i] = $abcd[$i]; //php7.4 之前版本可以使用{}
        }
        $arrlen = count($arr);
        for ($j = 0; $j < $arrlen; $j++)
        {
            $ch = $this->n2c($arr[$arrlen - 1 - $j]); //从后向前转汉字

            if ($ch === "零" && $flag === 0)
            { //如果是第一个零
                $flag = 1; //该位为零
                $str = $ch . $str; //加入汉字数值字符串
                continue;
            }

            if ($ch === "零")
            { //如果不是第一个零了
                continue;
            }
            $flag = 0; //该位不是零
            switch ($j)
            {
                case 0:
                    $str = $ch;
                    $flag_end = 0;
                    break; //第一位（末尾），没有以“零”结尾
                case 1:
                    if ($ch === '一')
                    {
                        $ch = '';
                    }
                    $str = $ch . "十" . $str;
                    break; //第二位
                case 2:
                    $str = $ch . "百" . $str;
                    break; //第三位
                case 3:
                    $str = $ch . "千" . $str;
                    break; //第四位
            }
        }
        if ($flag_end === 1) //如果以“零”结尾
        {
            mb_internal_encoding("UTF-8");
            $str = mb_substr($str, 0, -1); //把“零”去掉
        }
        return $str;
    }

    /**
     * 单个数字变汉字
     * @param $x
     * @return string
     */
    public function n2c($x)
    {
        $arr_n = ["零", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十"];
        return $arr_n[$x];
    }

    /**
     * 账户注册检查在不在注销的冷却时间中 是否允许注册 2.2.0 版本开始
     * @param array $where ['tel'=>17600303030] | ['unionid'=>''] | ['qq_unionid'=>''] | ['wuid'=>'']
     * @return array
     */
    public static function registCheckLogoutCD(array $where): array
    {
        $appConfig = config('app');
        $tel = $where['tel'] ?? '';
        $env = Env::get('host.name', '');
        if (self::CheckAppVersion('2.2.0') === '小' || $env !== 'online' || in_array($tel, $appConfig['mobile'], false))
        {
            return ['result' => true];
        }

        $timeNow = time();

        $logoutTime = $timeNow - 864000; // 注销10天内不能再注销

        $resTime = (new ZlUserZx())->where($where)->where('create_time', '>', $logoutTime)->order('id desc')->value('create_time');

        if ($resTime)
        {
            $cdTime = $resTime + 864000 - $timeNow; // 计算还有多长时间才能再注册

            $hour = ceil($cdTime % 86400 / 3600);

            if ($hour >= 24)
            {
                $timeTxt = ceil($cdTime / 86400) . '天';
            } else
            {
                $timeTxt = floor($cdTime / 86400) . '天' . $hour . '小时';
            }


            return ['result' => false, 'msg' => '当前无法注册，请在' . $timeTxt . '后注册'];
        }

        return ['result' => true];
    }

    /**
     * 账户注册检查在不在注销的冷却时间中 是否允许注册
     * @param array $where ['tel'=>17600303030] | ['unionid'=>''] | ['qq_unionid'=>''] | ['wuid'=>'']
     * @return array
     */
    public static function ZyhsRegistCheckLogoutCD(array $where): array
    {
        $appConfig = config('app');
        $tel = $where['tel'] ?? '';
        $env = Env::get('host.name', '');
        if ($env !== 'online' || in_array($tel, $appConfig['mobile'], false))
        {
            return ['result' => true];
        }

        $timeNow = time();
        $logoutTime = $timeNow - 864000; // 注销10天内不能再注销
        $resTime = (new ZyhsUserZx())->where($where)->where('create_time', '>', $logoutTime)->order('id desc')->value('create_time');

        if ($resTime)
        {
            $cdTime = $resTime + 864000 - $timeNow; // 计算还有多长时间才能再注册

            $hour = ceil($cdTime % 86400 / 3600);

            if ($hour >= 24)
            {
                $timeTxt = ceil($cdTime / 86400) . '天';
            } else
            {
                $timeTxt = floor($cdTime / 86400) . '天' . $hour . '小时';
            }


            return ['result' => false, 'msg' => '当前无法注册，请在' . $timeTxt . '后注册'];
        }

        return ['result' => true];
    }

    /**
     * 测试获取当前时间 TODO 上线后修改为只获取time()的时间 不在缓存里面获取时间
     * @param string $year
     * @return int
     */
    public static function testGetTime(string $year = ''): int
    {
        $date = Cache::get('test:time:year' . $year);

        return $date ? strtotime($date) ?: time() : time();
    }

    /**
     * 创建定制报告ID
     * @return string
     * @throws Exception
     */
    public static function createCustomReportId(): string
    {
        $time = request()->time();
        return (string)($time - 1600000000) . (string)(random_int(1000000, 9999999));
    }

    /**
     * 获取占星师 定制报告 预计收益
     * @param string $orderNum 订单编号
     * @param float $proportion 收益比例
     * @return int|float
     * @throws Exception
     */
    public static function getAstrCustomReportEarnings(string $orderNum, float $proportion)
    {
        $orderInfo = (new \app\common\cachemodel\Orders())->getInfoById($orderNum, ['total_amount']);

        if ($orderInfo && isset($orderInfo['total_amount']) && $orderInfo['total_amount'])
        {
            $amount = sprintf("%.2f", ceil($proportion * $orderInfo['total_amount'] * 100) / 100);

            return self::getAfterTaxAmount($amount);
        }

        return 0;
    }

    /**
     * 登陆日志记录
     * @param int $uid
     * @param int $loginType 登录方式 1=>微信，2=>qq,3=>微博 4=>手机号  5=>苹果登陆 6=>手机号一键登录
     * @param int $status 1=>正常 0=>删除 2=>开关关闭记录数据
     * @param string $ip 登陆ip地址
     */
    public static function loginRecordSave(int $uid, int $loginType, int $status, string $ip): void
    {
        $headers = request()->header();

        $data = [
            'uid' => $uid,
            'status' => $status,
            'mobile_type' => $headers['model'] ?? '',
            'login_type' => $loginType,
            'version' => $headers['version'] ?? '',
            'login_ip' => $ip,
            'create_time' => time(),
        ];

        (new ZlUserLoginRecord())->saveData($data);
    }

    /**
     * 获取用户账户密码错误剩余次数
     * @param int $uid
     * @param int $type 1=> 获取剩余错误次数 2=>错误次数+1 3=>错误次数清零
     * @return false|int|mixed
     */
    public static function getAccountResidualErrorNum(int $uid, int $type)
    {
        $config = config('cache.user')['coin'];
        $expire = 86400; // 过期时间
        $maxNum = $config['today_password_error_max_num'];
        $cacheKey = $config['today_password_error_num'] . date('Ymd') . ':' . $uid;

        switch ($type)
        {
            case 1:
                $num = Cache::get($cacheKey, 0);
                $resNum = $maxNum - $num;
                break;
            case 2:
                if (Cache::has($cacheKey))
                {
                    $num = Cache::inc($cacheKey, 1);
                    $resNum = $maxNum - $num;
                } else
                {
                    Cache::set($cacheKey, 1, $expire);
                    $resNum = $maxNum - 1;
                }
                break;
            case 3:
                Cache::rm($cacheKey);
                $resNum = $maxNum;
                break;
        }

        return $resNum;
    }

    /**
     * 身份核验二要素
     * @param $name
     * @param $idCard
     * @return array|false[]
     */
    public static function checkIdentityTwoElements($name, $idCard)
    {
        $appcode = '7dd1391c76eb403299b4afba18aabe59';
        $headers = [];
        $headers[] = "Authorization:APPCODE " . $appcode;
        $host = "https://idcard.market.alicloudapi.com";
        $path = "/lianzhuo/idcard";
        $headers = [];
        $headers[] = "Authorization:APPCODE " . $appcode;
        $querys = "cardno={$idCard}&name={$name}";
        $url = $host . $path . "?" . $querys;
        $res = self::curlRequest('GET', $url, [], $headers);
        if (!$res)
        {
            Log::write('身份证二要素使用量可能没了 - 济南联卓信息技术有限公司，请联系Mark确认！');
        }
        $resArr = json_decode($res, true);
        if ($resArr['resp']['code'] === 0)
        {
            return [
                'status' => true,
                'extend' => $res,
            ];
        }
        return [
            'status' => false,
        ];
    }

    /**
     * 银行卡信息核验
     * @url  https://market.aliyun.com/products/57000002/cmapi00037886.html?spm=5176.2020520132.101.2.f5057218q0flqP#sku=yuncode3188600001
     * @param $bankIdCard
     * @param $idCard
     * @param $name
     * @return array|false[]
     */
    public static function checkBankIdentityThreeElements($bankIdCard, $idCard, $name)
    {
        $host = "https://zball.market.alicloudapi.com";
        $path = "/v2/bcheck";
        $appcode = "7dd1391c76eb403299b4afba18aabe59";
        $headers = [];
        $headers[] = "Authorization:APPCODE " . $appcode;
        //根据API的要求，定义相对应的Content-Type
        $headers[] = "Content-Type" . ":" . "application/x-www-form-urlencoded; charset=UTF-8";
        $bodys = "accountNo={$bankIdCard}&idCardCode={$idCard}&name={$name}";
        $url = $host . $path;
        $res = self::curlRequest('POST', $url, $bodys, $headers);
        $resArr = json_decode($res, true);
        if ($resArr && $resArr['result']['respCode'] === 'T')
        {
            return [
                'status' => true,
                'bankName' => $resArr['result']['bancardInfor']['bankName'],
                'msg' => $resArr['result']['respMsg'],
                'data' => $resArr['result']['bancardInfor'],
            ];
        }

        if ($resArr && $resArr['result']['detailCode'])
        {
            Log::write('银行卡认证失败 err=' . $resArr['result']['respMsg'] . '$bankIdCard=' . $bankIdCard . ',$idCard=' . $idCard . ',$name=' . $name, 'error', true);
            switch ($resArr['result']['detailCode'])
            {
                case 0:
                    $msg = '认证成功';
                    break;
                case 1:
                    $msg = '信息有误';
                    break;
                case 2:
                    $msg = '卡状态异常';
                    break;
                case 3:
                    $msg = '发卡行不支持此交易';
                    break;
                case 4:
                    $msg = '银联不支持该银行';
                    break;
                case 5:
                    $msg = '无效卡';
                    break;
                case 6:
                    $msg = '受限制的卡';
                    break;
                case 7:
                    $msg = '姓名格式有误';
                    break;
                case 8:
                    $msg = '身份证号码格式有误';
                    break;
                case 9:
                    $msg = '手机号码格式有误';
                    break;
                case 10:
                    $msg = '银行卡格式有误';
                    break;
                case 11:
                    $msg = '密码错误次数超限';
                    break;
                case 12:
                    $msg = '验证要素格式有误';
                    break;
//            case 100:
//                $msg = '其他原因不通过';
//                break;
                default:
                    $msg = '其他原因不通过';
                    break;
            }
            return [
                'status' => false,
                'bankName' => '',
                'msg' => $msg,
            ];
        }

        Log::write('银行卡认证失败 $res=null（检查阿里云市场接口数量是不是用完了）' . '$bankIdCard=' . $bankIdCard . ',$idCard=' . $idCard . ',$name=' . $name, 'error', true);
        return [
            'status' => false,
            'bankName' => '',
            'msg' => '认证银行卡失败',
        ];
    }

    /**
     * 卡号查询总行联行号+联行号查询开户行
     * @url  https://market.aliyun.com/products/57000002/cmapi017567.html?spm=5176.2020520132.101.3.65897218y9aN25#sku=yuncode1156700000
     * @param $bankno
     * @param string $bankName 银行卡名字
     * @param string $keyword 支行关键字
     * @param int $page
     * @param string $province
     * @param string $city
     * @return \think\response\Json
     */
    public static function checkBankBranchInfo($bankno, $bankName = '', $keyword = '', $page = 1, $province = '', $city = '')
    {

        if ($bankName === "")
        {
            return show(0, '请选择开户行！');
        }

        //卡号查询总行联行号
        $host = "https://cnaps.market.alicloudapi.com";
        $path = "/lianzhuo/querybankaps";
        $appcode = "7dd1391c76eb403299b4afba18aabe59";
        $headers = [];
        $headers[] = "Authorization:APPCODE " . $appcode;
        //根据API的要求，定义相对应的Content-Type
        $headers[] = "Content-Type" . ":" . "application/x-www-form-urlencoded; charset=UTF-8";
        $querys = "card={$bankno}";
        $querys .= $bankName ? "&bank={$bankName}" : '';
        $querys .= $keyword ? "&key={$keyword}" : '';
        $querys .= "&page={$page}";
        $querys .= $province ? "&province={$province}" : '';
        $querys .= $city ? "&city={$city}" : '';
        $url = $host . $path;
        $res = self::curlRequest('GET', $url . '?' . $querys, [], $headers);
        $resArr = json_decode($res, true);
        if ($resArr['resp']['RespCode'] === '200')
        {
            if ($resArr['data']['record'][0]['bank'] !== $bankName)
            {
                if (strpos($resArr['data']['record'][0]['bank'], $bankName) === false)
                {//不包含 解析出来有可能是 中国工商银行 建设银行解析出来是 中国建设银行
                    Log::write('用户选择支行信息的时候，支持的银行卡错误（需要更新占星师前端的银行卡列表 或者 去掉银行名字的验证）：用户选择的银行=' . $bankName . '，查询云市场的支行名字为：' . $resArr['data']['record'][0]['bank'] . ',$bankno=' . $bankno . ',$bankName=' . $bankName . ',$keyword=' . $keyword . ',$page=' . $page . ',$province=' . $province . ',$city=' . $city, 'error', true);
                    return show(0, '开户行错误，请重新选择');
                }

                return show(200, '请求成功', $resArr['data']);
            }

            return show(200, '请求成功', $resArr['data']);
        }

        if (isset($resArr['resp']))
        {
            Log::write('查询支行列表失败：' . $resArr['resp']['RespMsg'] . ',$bankno=' . $bankno . ',$bankName=' . $bankName . ',$keyword=' . $keyword . ',$page=' . $page . ',$province=' . $province . ',$city=' . $city, 'error', true);
            return show(0, $resArr['resp']['RespMsg']);
        }

        Log::write('查询支行错误，请检查银行卡填写是否正确！' . ',$bankno=' . $bankno . ',$bankName=' . $bankName . ',$keyword=' . $keyword . ',$page=' . $page . ',$province=' . $province . ',$city=' . $city, 'error', true);
        return show(0, '查询支行错误，请检查银行卡填写是否正确！');
    }

    /**
     * 根据联行号，查询开户行信息
     * @param $bankcode
     * @return array
     */
    public static function checkBankCode($bankcode)
    {
        if ($bankcode)
        {
            //联行号查询开户行
            $host = "https://cnaps.market.alicloudapi.com";
            $path = "/lundroid/querybankcode";
            $querys = "bankcode={$bankcode}";
            $appcode = "7dd1391c76eb403299b4afba18aabe59";
            $headers[] = "Authorization:APPCODE " . $appcode;
            //根据API的要求，定义相对应的Content-Type
            $headers[] = "Content-Type" . ":" . "application/x-www-form-urlencoded; charset=UTF-8";
            $url = $host . $path;
            $res = self::curlRequest('GET', $url . '?' . $querys, [], $headers);
            $resArr = json_decode($res, true);
            if ($resArr['resp']['RespCode'] === '200')
            {
                $data = $resArr['data'][0];
                return [
                    'status' => true,
                    'msg' => $resArr['resp']['RespMsg'],
                    'data' => $data,
                ];
            }

            Log::write('联行号查询开户行失败：' . $resArr['resp']['RespMsg'], 'error', true);
        } else
        {
            Log::write('联行号不能为空', 'error', true);
            return [
                'status' => false,
                'msg' => '联行号不能为空！',
            ];
        }
    }

    /**
     * 根据 月份 和 天，返回星座index 1~12
     * @param int $month 月份
     * @param int $day 天
     * @return int 星座index 1~12
     */
    public static function getSignIndex($month = 1, $day = 1)
    {
        $sign_index = 1;
        if (($month == 3 || $month == 4) && ($day > 22 || $day < 21))
        {
            $sign_index = 1;
        } elseif (($month == 4 || $month == 5) && ($day > 22 || $day < 22))
        {
            $sign_index = 2;
        } elseif (($month == 5 || $month == 6) && ($day > 23 || $day < 22))
        {
            $sign_index = 3;
        } elseif (($month == 6 || $month == 7) && ($day > 23 || $day < 23))
        {
            $sign_index = 4;
        } elseif (($month == 7 || $month == 8) && ($day > 24 || $day < 22))
        {
            $sign_index = 5;
        } elseif (($month == 8 || $month == 9) && ($day > 23 || $day < 24))
        {
            $sign_index = 6;
        } elseif (($month == 9 || $month == 10) && ($day > 25 || $day < 24))
        {
            $sign_index = 7;
        } elseif (($month == 10 || $month == 11) && ($day > 25 || $day < 23))
        {
            $sign_index = 8;
        } elseif (($month == 11 || $month == 12) && ($day > 24 || $day < 23))
        {
            $sign_index = 9;
        } elseif (($month == 12 || $month == 1) && ($day > 24 || $day < 21))
        {
            $sign_index = 10;
        } elseif (($month == 1 || $month == 2) && ($day > 22 || $day < 20))
        {
            $sign_index = 11;
        } elseif (($month == 2 || $month == 3) && ($day > 21 || $day < 21))
        {
            $sign_index = 12;
        }
        return $sign_index;
    }


    /**
     * 身份证OCR识别-身份证图片识别
     * @url  https://market.aliyun.com/products/57000002/cmapi033391.html?spm=5176.730005.productlist.d_cmapi033391.b7823524zM7jsY&innerSource=search_%E6%89%8B%E6%8C%81%E8%BA%AB%E4%BB%BD%E8%AF%81#sku=yuncode2739100003
     * @param string $image base64编码的二进制图片(需要注意:图片的base64编码是不包含图片头的，如data:image/jpg;base64,) 图片大小尽量等比例压缩控制在200K左右
     * @param string $side front：身份证带人脸一面，back：身份证带国徽片一面
     * @return array|false[]
     */
    public static function checkIdCardPic($image = '', $side = 'front')
    {
        $image_encode = urlencode($image);
        $host = "https://yixi.market.alicloudapi.com";
        $path = "/ocr/idcardocr";//英文
//        $path = "/ocr/idcard";//中文
        $appcode = "7dd1391c76eb403299b4afba18aabe59";
        $headers = [];
        $headers[] = "Authorization:APPCODE " . $appcode;
        //根据API的要求，定义相对应的Content-Type
        $headers[] = "Content-Type" . ":" . "application/x-www-form-urlencoded; charset=UTF-8";
        $bodys = "image={$image_encode}&side={$side}";
        $url = $host . $path;
        $res = self::curlRequest('POST', $url, $bodys, $headers);
        $resArr = json_decode($res, true);
        if ($resArr['code'] === 200)
        {
            unset($resArr['data']['photo']);//这个数据用不到，所以干掉
            return [
                'status' => true,
                'data' => $resArr['data'],
            ];
        }

        Log::write('身份证OCR识别side=' . $side . '，失败：' . json_encode($resArr, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . ',图片的base64编码=[' . $image . ']', 'error', true);
        return [
            'status' => false,
            'msg' => $resArr['msg'],
        ];
    }


    /**
     * 手持身份证人脸照片比对-身份证头像比对
     * @url  https://market.aliyun.com/products/57126001/cmapi00037006.html?spm=5176.730005.productlist.d_cmapi00037006.b7823524zM7jsY&innerSource=search_%E6%89%8B%E6%8C%81%E8%BA%AB%E4%BB%BD%E8%AF%81#sku=yuncode3100600001
     * @param string $image base64编码的二进制图片(需要注意:图片的base64编码是不包含图片头的，如data:image/jpg;base64,) 图片大小尽量等比例压缩控制在200K左右
     * @return array|false[]
     */
    public static function checkIdCardHandPic($image = '')
    {
        $img_encode = urlencode($image);
        $host = "https://handhold.shumaidata.com";
        $path = "/handhold";
        $appcode = "7dd1391c76eb403299b4afba18aabe59";
        $headers = [];
        $headers[] = "Authorization:APPCODE " . $appcode;
        //根据API的要求，定义相对应的Content-Type
        $headers[] = "Content-Type" . ":" . "application/x-www-form-urlencoded; charset=UTF-8";
        $bodys = "image=data:image/jpg;base64,{$img_encode}";
        $url = $host . $path;
        $res = self::curlRequest('POST', $url, $bodys, $headers);
        $resArr = json_decode($res, true);
        if ($resArr['code'] === 200)
        {
            return [
                'status' => true,
                'data' => $resArr['data'],
            ];
        }

        Log::write('手持身份证人脸照片比对-身份证头像比对失败：' . $res . ',图片的base64编码=[' . $image . ']', 'error', true);
        if ($resArr['msg'] === 'INVALID_IMAGE_FORMAT_OR_SIZE')
        {
            return [
                'status' => false,
                'msg' => '图片格式错误或者图片过大！',
            ];
        }
        return [
            'status' => false,
        ];
    }


    /**
     * 网络图转base64编码
     * @param string $img_url
     * @return false|string
     */
    public static function imgUrlToBase64($img_url = '')
    {
        if (!$img_url)
        {
            return false;
        }
//        return chunk_split(base64_encode(file_get_contents($img_url)));
        return base64_encode(file_get_contents($img_url));
    }

    /**
     * 定制报告发送消息
     * @param int $type 0：用户提交问题；1：占星师认证简答题提交；2：简答题审核（gva）；3：占星师接单；4：占星师回答问题；5：审核占星师回答；(后台)；6：用户退款；7：占星师考试，选择题答题审核结果；8：补充问题异常
     * @param array $data 需要的参数
     * @param string $terminal 终端类型：PC、WEB、WEAPP、ALIPAY
     */
    public static function sendZlcrMsg($type = 100, $data = [], $terminal = '')
    {
        $url = 'https://oapi.dingtalk.com/robot/send?access_token=29bfef4d50fb3890b24d1e1c15d1bdbddc0ea3c43b188bb2ddfaa3403593963d';// 钉钉推送地址
        $terminalStr = '';
        if ($terminal)
        {
            $terminalStr = '-' . $terminal;
        }
        $env = Env::get('host.name', 'local');
        $title = '[' . $env . $terminalStr . ']定制报告通知:';

        switch ($type)
        {
            case 0:
                $contentMsg = '用户提交问题';
                $herfUrl = '/admin/zlcr_custom_report_question/index.html';
                break;
            case 1:
                $contentMsg = '占星师认证简答题提交';
                $herfUrl = '/admin/gva.gva_controller/common_zlcrastrexerciserecord.html';
                break;
            case 3:
                $contentMsg = '占星师接单';
                $herfUrl = '/admin/zlcr_custom_comment_refund/index.html';
                break;
            case 4:
                $contentMsg = '占星师回答问题';
                $herfUrl = '/admin/zlcr_custom_comment_refund/index.html';
                break;
            case 5:
                $contentMsg = '审核占星师回答';
                $herfUrl = '/admin/zlcr_custom_comment_refund/index.html';
                break;
            case 6:
                $contentMsg = '用户退款';
                $herfUrl = '/admin/zlcr_custom_report_question/index.html';
                break;
            case 7:
                return;
//                $contentMsg = '占星师考试选择题结果';
//                $herfUrl = '/admin/zlcr_astr/index.html';
//                break;
            case 8:
                $title .= '补充问题异常';
                //参数循环列出
                $msg = '';
                foreach ($data as $v)
                {
                    $msg .= $v . PHP_EOL;
                }
                if ($msg == '')
                {
                    $msg = 'test';
                }
                self::sendDingMsg($url, $title, $msg);
                return;
            default:
                $contentMsg = 'sendZlcrMsg[type]参数传错了，请检查代码！';
                $herfUrl = '/admin/zlcr_custom_comment_refund/index.html';
                break;
        }

        $title .= $contentMsg;

        //参数循环列出
        $msg = '';
        foreach ($data as $v)
        {
            $msg .= $v . PHP_EOL;
        }
        if ($msg == '')
        {
            $msg = 'test';
        }

        self::sendDingMsg($url, $title, $msg, 2, Env::get('host.admin_domain') . $herfUrl);
    }

    /**
     * 获取合盘报告入库是否开启开关
     * @return bool true=>开启 false=>关闭
     * @throws Exception
     */
    public static function getSynastryReportSwitch(): bool
    {
        $config = (new AppConfig())->configInfo('synastry_report_switch');

        return (isset($config['synastry_report_switch']) && (int)$config['synastry_report_switch'] === 1);
    }

    /**
     * php显示指定长度的字符串，超出长度以省略号(...)填补尾部显示
     * @param string $str 字符串
     * @param int $len 指定长度
     * @return string
     */
    public static function cutSubstr($str = '', $len = 30)
    {
        if (mb_strlen($str) > $len)
        {
            $str = mb_substr($str, 0, $len - 1) . '...';
        }
        return $str;
    }

    /**
     * 获取分数百分比和颜色
     * @param int $score
     * @return array
     */
    public static function getScorePercentInfo(int $score): array
    {
        $colors = [
            0 => ['name' => '困难', 'color' => '#7775ff',],
            1 => ['name' => '警戒', 'color' => '#ffa536',],
            2 => ['name' => '平平', 'color' => '#ffed17',],
            3 => ['name' => '吉利', 'color' => '#00f4e7',],
            4 => ['name' => '大吉', 'color' => '#00e9a0',],
        ];

        // $level = (int)(floor($score / 20));
        switch (true)
        {
            case $score >= 65:
                $level = 4;
                break;
            case $score >= 51:
                $level = 3;
                break;
            case $score >= 45:
                $level = 2;
                break;
            case $score >= 33:
                $level = 1;
                break;
            default:
                $level = 0;
        }
        // $resCcore = $score % 20;
        //
        // if ($score > 50)
        // { // 平平往上 大吉
        //     $percent = (int)(($resCcore / 20) * 100);
        // } else
        // { // 平平往下 困难
        //     $percent = (int)(((20 - $resCcore) / 20) * 100);
        // }

        $percent = $score; // 百分比

        $level = $level >= 4 ? 4 : $level;

        return [
            'score' => $score,
            'percent' => $percent,
            'color' => $colors[$level]['color'],
        ];
    }

    /**
     * 获取分数百分比和颜色
     * @param int $score
     * @return array
     */
    public static function getScorePercentInfo2(int $score): array
    {
        $colors = [
            0 => ['name' => '困难', 'color' => '#7775ff',],
            1 => ['name' => '警戒', 'color' => '#ffa536',],
            2 => ['name' => '平平', 'color' => '#ffed17',],
            3 => ['name' => '吉利', 'color' => '#00f4e7',],
            4 => ['name' => '大吉', 'color' => '#00e9a0',],
        ];

        // $level = (int)(floor($score / 20));
        switch (true)
        {
            case $score >= 93:
                $level = 4;
                break;
            case $score >= 85:
                $level = 3;
                break;
            case $score >= 77:
                $level = 2;
                break;
            case $score >= 69:
                $level = 1;
                break;
            default:
                $level = 0;
        }
        // $resCcore = $score % 20;
        //
        // if ($score > 50)
        // { // 平平往上 大吉
        //     $percent = (int)(($resCcore / 20) * 100);
        // } else
        // { // 平平往下 困难
        //     $percent = (int)(((20 - $resCcore) / 20) * 100);
        // }

        $percent = $score; // 百分比

        $level = $level >= 4 ? 4 : $level;

        return [
            'score' => $score,
            'percent' => $percent,
            'color' => $colors[$level]['color'],
        ];
    }

    /**
     * 发送营销类消息
     * @param array $mobileList
     * @param string $extend
     * @param int $tpId
     * @param $tpContent
     * @return array
     * @throws GuzzleException
     */
    public static function sendYxSms($mobileList, $extend, $tpId, $tpContent)
    {
        $username = 'xzns666yx';
        $password = 'qh3QMByHU1mznTXv';
        $time = time();
        $records = [];
        foreach ($mobileList as $value)
        {
            $records[] = [
                'mobile' => $value,
                'tpContent' => $tpContent ?: (object)[],
            ];
        }

        $data = [
            'username' => $username,
            'password' => md5(md5($password) . $time),
            'tKey' => $time,
            'signature' => '【准了】',
            'tpId' => $tpId,
            'extend' => $extend,
            'records' => $records,
        ];

        // 返回数据结构样式
        //array (
        //   'success' => true,
        //   'data' =>
        //   array (
        //     'code' => 200,
        //     'msg' => 'success',
        //     'tpId' => '19379',
        //     'msgId' => '159340296571268216064',
        //     'invalidList' =>
        //     array (
        //     ),
        //   ),
        // )

        return Curl::request('https://api.mix2.zthysms.com/v2/sendSmsTp', $data, 'POST', [], 'json');
    }

    /**
     * 发送营销类消息
     * @param array $mobileList
     * @param string $extend
     * @param int $tpId
     * @param $tpContent
     * @param string $signature 短信签名    准了｜准有好事
     * @return array
     * @throws GuzzleException
     */
    public static function sendYxSms2($mobileList, $extend, $tpId, $tpContent, $signature = '【准了】')
    {
        $username = 'xzns666yx';
        $password = 'qh3QMByHU1mznTXv';
        $time = time();
        $records = [];
        foreach ($mobileList as $value)
        {
            $records[] = [
                'mobile' => $value,
                'tpContent' => $tpContent ?: (object)[],
            ];
        }

        $data = [
            'username' => $username,
            'password' => md5(md5($password) . $time),
            'tKey' => $time,
            'signature' => $signature,
            'tpId' => $tpId,
            'extend' => $extend,
            'records' => $records,
        ];

        // 返回数据结构样式
        //array (
        //   'success' => true,
        //   'data' =>
        //   array (
        //     'code' => 200,
        //     'msg' => 'success',
        //     'tpId' => '19379',
        //     'msgId' => '159340296571268216064',
        //     'invalidList' =>
        //     array (
        //     ),
        //   ),
        // )

        return Curl::request('https://api.mix2.zthysms.com/v2/sendSmsTp', $data, 'POST', [], 'json');
    }

    /**
     * 发送行业类消息
     * @param array $mobileList
     * @param string $extend
     * @param int $tpId
     * @param $tpContent
     * @return array
     * @throws GuzzleException
     */
    public static function sendHySms($mobileList, $extend, $tpId, $tpContent, $signature = '')
    {
        $username = 'xzns666hy';
        $password = 'x7TYRFWi';
        $time = time();
        $records = [];
        foreach ($mobileList as $value)
        {
            $records[] = [
                'mobile' => $value,
                'tpContent' => $tpContent ?: (object)[],
            ];
        }

        if (!$signature)
        {
            if (in_array($tpId, [
                50134,  // Beta私域H5审核失败提醒
                50132,  // Beta私域H5占星师新任务提醒
                49273,  // 私域 h5用户端和占星师端验证码 登录验证码
                49272,  // 私域H5审核失败提醒
                49271,  // 私域H5占星师新任务提醒
                128347,  // 私域H5占星师6小时未回复提醒
            ], true))
            {
                $signature = '【星座女神咨询服务】';
            } else
            {
                $signature = '【准了】';
            }
        }

        $data = [
            'username' => $username,
            'password' => md5(md5($password) . $time),
            'tKey' => $time,
            'signature' => $signature,
            'tpId' => $tpId,
            'extend' => $extend,
            'records' => $records,
        ];

        // 返回数据结构样式
        //array (
        //   'success' => true,
        //   'data' =>
        //   array (
        //     'code' => 200,
        //     'msg' => 'success',
        //     'tpId' => '19379',
        //     'msgId' => '159340296571268216064',
        //     'invalidList' =>
        //     array (
        //     ),
        //   ),
        // )

        return Curl::request('https://api.mix2.zthysms.com/v2/sendSmsTp', $data, 'POST', [], 'json');
    }

    /**
     * 获取税后金额
     * @param $amount
     * @return float
     */
    public static function getAfterTaxAmount($amount): float
    {
        $resAmount = $amount - ($amount * 0.01) - ($amount * 0.0672);

        return (float)(sprintf("%.2f", $resAmount));
    }

    /**
     * 获取运势结果档案信息
     * @param array $recodes
     * @param int $seifRid
     * @return array
     */
    public function getFortuneDetailRecodeInfo(array $recodes, int $seifRid = 0): array
    {
        $data = [];
        //oss资源地址
        $oss = Env::get('host.photo');
        foreach ($recodes as $recodeItem)
        {
            if ($recodeItem['timezone'])
            {
                if ($recodeItem['timezone'] > 0)
                {
                    $timezone = '东' . $this->numberChinese($recodeItem['timezone']) . '区 GMT+' . $recodeItem['timezone'];
                } else
                {
                    $timezone = '西' . $this->numberChinese(abs($recodeItem['timezone'])) . '区 GMT' . $recodeItem['timezone'];
                }
            } else
            {
                $timezone = '中时区 GMT0';
            }

            $longLat = ($recodeItem['ew'] == 1 ? 'E' : 'W') . "{$recodeItem['long_deg']}°{$recodeItem['long_min']}′ " . ($recodeItem['ns'] == 1 ? 'N' : 'S') . "{$recodeItem['lat_deg']}°{$recodeItem['lat_min']}′";

            $address = trim($recodeItem['birth_country'] . $recodeItem['birth_province'] . $recodeItem['birth_city']);

            $data[] = [
                'avatar' => $recodeItem['avatar'] ?: $oss . '/avatar_default_2.png',
                'name' => $seifRid == $recodeItem['id'] ? '自己' : $recodeItem['name'],
                'info_list' => [
                    [
                        'title' => '性别：',
                        'content' => $recodeItem['type'] == 1 ? '男' : '女',
                    ], [
                        'title' => '出生时间：',
                        'content' => $recodeItem['year'] . '.' . sprintf('%02d', $recodeItem['month']) . '.' . sprintf('%02d', $recodeItem['day']) . ' ' . sprintf('%02d', $recodeItem['hour']) . ':' . sprintf('%02d', $recodeItem['minute']),
                    ], [
                        'title' => '所在时区：',
                        'content' => $timezone,
                    ], [
                        'title' => '出生地点：',
                        'content' => empty($address) ? '地球' : $address,
                    ], [
                        'title' => '经纬度：',
                        'content' => $longLat,
                    ], [
                        'title' => '现居地点：',
                        'content' => $recodeItem['live_country'] . $recodeItem['live_province'] . $recodeItem['live_city'],
                    ],
                ],
            ];
        }

        return $data;
    }

    /**
     * 判断是否是公司公网ip
     * @return bool
     */
    public function isCmopanyIp()
    {
        $ip = $this->request->ip();
        if (in_array($ip, ['122.115.237.36', '103.90.176.228'], true))
        {
            return true;
        }
        return false;
    }

    /**
     * 订单按钮删除再次购买按钮
     * @param array $btns
     * @return array
     */
    public static function orderBtnReset(array $btns)
    {
        foreach ($btns as $k => $btn)
        {
            if (in_array($btn['type'], [5, 6], true))
            {
                unset($btns[$k]);
            }
        }

        return array_values($btns);
    }

    /**
     * 钉钉群消息发送
     * @param string $title 标题
     * @param string $contentMsg 内容
     * @param string $herfUrl 点击消息跳转地址
     * @param string $groupName 钉钉群名类型
     * @return array|bool|string
     */
    public static function resDingDingMsgSend(string $title, string $contentMsg, string $herfUrl, string $groupName, int $type = 1)
    {
        // $time = time() * 1000;
        $t = microtime();
        $time = substr($t, 11, 10) . substr($t, 2, 3);
        switch ($groupName)
        {
            case 'private_area': // 私域H5
                $messageType = [
                    1 => [  // 用户支付成功
                        'sec' => 'SEC2e07d5efb62c950d1eba996169639b536bad29d50f40d32c5b4b0a7aa5faaffd',
                        'url' => 'https://oapi.dingtalk.com/robot/send?access_token=1a141331254417154681c55335e74be41a44fb1da65aa92493fee9d3f752fb65',
                    ],
                    2 => [  // 派单成功
                        'sec' => 'SEC198403797e0fd2ffeafa137789786a24467adbeb489c6fb57a25438cbf90abec',
                        'url' => 'https://oapi.dingtalk.com/robot/send?access_token=78dea95d65d8138e9e1702c08c95a27a53daa7cf5673fa8031f8bc5cda53f0ee',
                    ],
                    3 => [  // 支付后12小时未派单
                        'sec' => 'SEC1f69509f4c11e110cca698c8bc2fbab9270ba45cb3cbac052d46380bba4074ff',
                        'url' => 'https://oapi.dingtalk.com/robot/send?access_token=a258b9cc7bc5c3d9fbd8ec300439ed35bc0b7882fe30e88d0afe8420f723b810',
                    ],
                    4 => [  // 后台重新派单
                        'sec' => 'SECc74bed5e1994cc0eb1c71d27e1ac280a60cd94ceedf945866d6f645e45df6ca4',
                        'url' => 'https://oapi.dingtalk.com/robot/send?access_token=fd39d6ded40ec959bd579de4e90c1d464baaf673472d364dc608c4d2d598e38e',
                    ],
                    5 => [  // 占星师超过12小时未回复
                        'sec' => 'SECf55254230d0a03ba6b35a2ae110ff3d1013c7dbf9e0e48fee9f5f7efae5e8185',
                        'url' => 'https://oapi.dingtalk.com/robot/send?access_token=c114404473ce07086d625405c38be24b3c6b08cdaaa1ee6987470bd45d63125e',
                    ],
                    6 => [  // 占星师回复
                        'sec' => 'SEC9e219852838b5773f627eea989fea58f89dff50eda46a3c1a9fa6458cf9550b2',
                        'url' => 'https://oapi.dingtalk.com/robot/send?access_token=e557e41773f22d3d62d85e761fff0eebb7d1bbe14f47f41fc42af76a2d02e88c',
                    ],
                    7 => [  // 审核通过
                        'sec' => 'SEC3376b4dbe982e987d9e95eca26caaa26803a9370ed3463a24f46985d739e9348',
                        'url' => 'https://oapi.dingtalk.com/robot/send?access_token=b2a44dca7ea952ef31f39828fc7be89c82b410a426de9b3ff599a86ae6580c81',
                    ],
                    8 => [  // 审核未通过
                        'sec' => 'SECbc4dd0f1b62889f0a5fdc604894684487edafd5231b7db935bb64eb944ba9b72',
                        'url' => 'https://oapi.dingtalk.com/robot/send?access_token=32bfe7fd672b901a14ecb60e99c2f7d29b782f966b44f68a2fe411ad090c0beb',
                    ],
                    9 => [  // 超12小时未审核
                        'sec' => 'SEC93289e4bfa0514d28d38db1c8391cdebe594f94fd37cbf0656fe33a3d327d7ab',
                        'url' => 'https://oapi.dingtalk.com/robot/send?access_token=c16a1837c9794c7ee6c95f09962a243f83d21e30eb26247f29ad224a09dac63a',
                    ],
                ];
                break;
            default:
                return false;
        }

        if (isset($messageType[$type]))
        {
            //  用户下单成功通知消息 钉钉消息
            $sec = $messageType[$type]['sec'];
            $url = $messageType[$type]['url'];// 钉钉推送地址
        } else
        {
            return false;
        }

        $sign = urldecode(base64_encode(hash_hmac('sha256', $time . "\n" . $sec, $sec, true)));
        $url .= "&timestamp={$time}&sign={$sign}";

        return self::sendDingMsg($url, $title, $contentMsg, 2, $herfUrl);
    }

    /**
     * 创建问答咨询ID
     * @return string
     */
    public static function createQuestionAnswerId(): string
    {
        $time = request()->time();
        return (string)($time - 1640000000) . (string)(random_int(1000000, 9999999));
    }

    /**
     * @param int $birthYear 出生的年 2022
     * @param int $birthMonth 出生的月   12
     * @param int $birthDay 出生的日  11
     * @param int $type 1的时候是虚岁,2的时候是周岁
     * @return int
     */
    public static function getAgeByBirth(int $birthYear, int $birthMonth, int $birthDay, $type = 1)
    {
        $nowYear = date("Y");
        $nowMonth = date("m");
        $nowDay = date("d");
        if ($type == 1)
        {
            $age = $nowYear - ($birthYear - 1);
        } else
        {
            if ($nowMonth < $birthMonth)
            {
                $age = $nowYear - $birthYear - 1;
            } elseif ($nowMonth == $birthMonth)
            {
                if ($nowDay < $birthDay)
                {
                    $age = $nowYear - $birthYear - 1;
                } else
                {
                    $age = $nowYear - $birthYear;
                }
            } else
            {
                $age = $nowYear - $birthYear;
            }
        }

        return $age;
    }

    /**
     * 问答 获取问题的追问 解答剩余时间
     * @param int $time 咨询时间
     * @return int
     */
    public static function getToAnswerTime(int $time)
    {
        $t = date('His', $time);
        $nowTime = time();

        if ($t < 80000)
        {
            $endTime = strtotime(date('Y-m-d 11:00:00', $time)) - $nowTime;  // 11点前回复
        } elseif ($t > 210000)
        {
            $endTime = strtotime(date('Y-m-d 11:00:00', strtotime('+1 day', $time))) - $nowTime;  // 次日11点前回复
        } else
        {
            $endTime = $time + 10800 - $nowTime;    // 白天时间 回答为3小时内
        }

        return $endTime;
    }

    /**
     * 准有好事 - 问答 获取问题的追问 解答剩余时间
     * @param int $time 咨询时间
     * @return int
     */
    public static function getZyhsToAnswerTime(int $time)
    {
        $t = date('His', $time);
        $nowTime = time();

        if ($t < 100000)
        {
            $endTime = strtotime(date('Y-m-d 11:00:00', $time)) - $nowTime;  // 11点前回复
        } elseif ($t > 200000)
        {
            $endTime = strtotime(date('Y-m-d 11:00:00', strtotime('+1 day', $time))) - $nowTime;  // 次日11点前回复
        } else
        {
            $endTime = $time + 10800 - $nowTime;    // 白天时间 回答为3小时内
        }

        return $endTime;
    }

    /**
     * 获取符合字段和字段值的数组集合
     * @param array $data 待过滤数组
     * @param string $field 要查找的字段
     * @param $value 要查找的字段值
     * @return array 返回所有符合要求的数组集合
     */
    public static function arrayFilterFieldValue(array $data, string $field, $value)
    {
        $data = array_filter($data, function ($row) use ($field, $value)
        {
            if (isset($row[$field]))
            {
                return $row[$field] == $value;
            }
        });
        return $data;
    }

    /**
     * 获取 字符串类型 随机ID
     * @return string
     * @throws Exception
     */
    public static function getStringRandomId(): string
    {
        $s = (time() - 1600000000) . random_int(100000, 900000);

        $scale = 34;    // 进制
        $sixteen = [
            10 => "A", 11 => "B", 12 => "C", 13 => "D", 14 => "E", 15 => "F",
            16 => 'G', 17 => 'H', 18 => 'I', 19 => 'J', 20 => 'K',
            21 => 'L', 22 => 'M', 23 => 'N', 24 => 'O', 25 => 'P',
            26 => 'Q', 27 => 'R', 28 => 'S', 29 => 'T', 30 => 'U',
            31 => 'V', 32 => 'W', 33 => 'X', 34 => 'Y', 35 => 'Z',
        ];

        $y = [];
        while ($s > 0)
        {
            $y[] = $s % $scale;
            $s = floor($s / $scale);
        }

        $str = "";
        $y_n = count($y);
        for ($i = $y_n - 1; $i >= 0; $i--)
        {
            if (array_key_exists($y[$i], $sixteen))
            {
                $str .= $sixteen[$y[$i]];
            } else
            {
                $str .= $y[$i];
            }
        }

        return $str;
    }

    /**
     * 获取档案唯一hash
     * @param array $recode
     * @return string
     */
    public static function getRecodeHashKey(array $recode)
    {
        $recodeUniqueKey = "{$recode['type']}:{$recode['year']}:{$recode['month']}:{$recode['day']}:{$recode['hour']}:{$recode['minute']}:{$recode['timezone']}:{$recode['ew']}:{$recode['long_deg']}:{$recode['long_min']}:{$recode['ns']}:{$recode['lat_deg']}:{$recode['lat_min']}:{$recode['is_summer']}";

        return md5($recodeUniqueKey);
    }

    /**
     * 获取 档案 当前年份所属阶段年
     * @param array $recode
     * @param int $year
     */
    public static function getRecodeStageyear(array $recode, int $year): array
    {
        $date = date('Y,m,d,H,i');
        [$Y, $M, $D, $H, $I] = explode(',', $date);
        $advance = [
            'year' => (int)$Y,
            'month' => (int)$M,
            'day' => (int)$D,
            'hour' => (int)$H,
            'minute' => (int)$I,
            'timezone' => 8,
            'is_summer' => 0,
        ];
        $option = [
            'recode' => $recode,
            'time_start' => 23, // 日辰初始   23=> 23点   或  0=>24点
            'time_type' => 0,   // 时间模式   0=>北京时间   1=>真太阳时
            'advance' => $advance,
        ];
        $data = HoroscopeData::getInstance($option)->getUserData();

        $startYear = (int)mb_substr($data['start_time'], 0, 4);
        $resData = [
            'num' => 0,
            'start_year' => 0,
            'end_year' => 0,
        ];

        if ($year >= $startYear)
        {   // 已经 进入起运年
            $n = (int)(ceil((($year + 1) - $startYear) / 10)); //
            $sY = $startYear + (($n - 1) * 10); // 当前所属阶段起始年
            if ($sY - $recode['year'] < 100)
            {   // 当前所属阶段起始 年 和用户实际年纪在 100 岁以内
                $resData = [
                    'num' => $n,
                    'start_year' => $sY,
                    'end_year' => $sY + 9,
                ];
            }
        }
        $arr_n = ["零", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十"];

        $resData['num_han'] = $arr_n[$resData['num']];

        return $resData;
    }

    /**
     * 是否是华为
     * @return bool
     */
    public static function IsHuawei(): bool
    {
        if (defined('IS_HUAWEI'))
        {
            return IS_HUAWEI;
        } else
        {
            $header = request()->header();
            $isHuawei = (isset($header['channel']) && $header['channel'] === 'huawei');
            define('IS_HUAWEI', $isHuawei);
            return IS_HUAWEI;
        }
    }

    /**
     * 是否是华为 合规版本 V1
     * @return bool
     */
    public static function IsHuaweiAndOppo(): bool
    {
        if (defined('IS_HUAWEI_OPPO'))
        {
            return IS_HUAWEI_OPPO;
        }
        $header = request()->header();
        $isHuawei = (isset($header['channel']) && in_array(strtolower($header['channel']), ['huawei', 'oppo'])) && self::CheckAppVersion('2.9.11') !== '小';
        define('IS_HUAWEI_OPPO', $isHuawei);
        return IS_HUAWEI_OPPO;
    }

    /**
     * 是否是OPPO
     * @return bool
     */
    public static function IsOppo(): bool
    {
        if (defined('IS_OPPO'))
        {
            return IS_OPPO;
        }
        $header = request()->header();
        $isOppo = isset($header['channel']) && $header['channel'] === 'OPPO' && self::CheckAppVersion('2.14.0') !== '小';
        define('IS_OPPO', $isOppo);
        return IS_OPPO;
    }

    /**
     * 是否是华为 合规版本 V1
     * @return bool
     * @throws \think\Exception
     */
    public static function huaweiIsShowAll(): bool
    {
        $header = request()->header();
        if (defined('IS_HUAWEI_SHOW_ALL'))
        {
            return IS_HUAWEI_SHOW_ALL;
        }

        if (!defined('USERINFO_UID'))
        {
            if (!empty($header['access-token']))
            {
                $accessUserToken = (new Aes())->decryptWithOpenssl($header['access-token']);
                if (!preg_match('/||/', $accessUserToken))
                {
                    return false;
                }
                [$token, $uid] = explode('||', $accessUserToken);
                if (!$uid)
                {
                    return false;
                }
                define('USERINFO_UID', $uid);
            } else
            {
                // 未登录
                return false;
            }
        }

        $uid = USERINFO_UID;
        if ($uid !== 13477830)
        {
            $orderNum = Cache::get('user:order_num' . $uid, false);
            if ($orderNum === false)
            {
                $orderNum = Db::table('wechat_backup_07_18_14_43_00.app_orders')->where('uid', $uid)->whereIn('status', [1, 4])->count();
                // 2分钟有效期
                Cache::set('user:order_num' . $uid, $orderNum, 120);
            }
            $HuaweiIsShowAll = $orderNum > 0 ? true : false;
            if (!$HuaweiIsShowAll)
            {
                if (isset($header['install-time']))
                {
                    $HuaweiIsShowAll = time() - $header['install-time'] > 86400 ? true : false;
                } else
                {
                    $HuaweiIsShowAll = false;
                }
            }
        } else
        {
            $HuaweiIsShowAll = false;
        }

        define('IS_HUAWEI_SHOW_ALL', $HuaweiIsShowAll);
        return IS_HUAWEI_SHOW_ALL;
    }

    /**
     * 获取当前请求路径
     * @return string
     */
    public static function requestAddress()
    {
        try
        {
            $model = request()->module();
            $controller = request()->controller();
            $startNum = strpos($controller, '.');
            if ($startNum !== false)
            {
                $controller = substr($controller, $startNum + 1);
            }
            $action = request()->action(); // 全小写
            $uri = $model . '/' . $controller . '/' . $action;
            if ($uri === '//')
            {
                $uri = !empty($_SERVER['argv']) ? implode(' ', $_SERVER['argv']) : '';
            }
            return $uri;
        }
        catch (\Exception $e)
        {
            return '';
        }
    }

    /**
     * 星盘解读 获取依据结果
     * @param array $recodeInfo
     * @param string $basis
     * @return string
     */
    public static function convertNatalBasisRes(array $recodeInfo, string $basis)
    {
        // 获取用户本命盘数据
        $userSetting['natal'] = json_decode('{"planet":[[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[1,0],[0,0],[0,0],[1,0]],"phase":{"0":[1,8],"60":[1,6],"90":[1,7],"120":[1,7],"180":[1,7],"45":[0,0],"135":[0,0],"30":[0,0],"150":[0,0],"72":[0,0],"144":[0,0]},"arabic":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"h_sys":"p","ecliptic":0,"firdaria":"default","profection_push_direction":0,"decanates":0,"bound_type":"default","ayanamsa":0}', true);
        $astroData = Astrolog::getInstance()
            ->setExecPrefix(config('astrolog.prefix'))
            ->setIsAstrolabe(true)
            ->setUserSetting($userSetting)
            ->setVersion(2)
            ->setRecode1($recodeInfo)
            ->setGetDatas(['natal'])
            ->getAstroDatas()
            ->getDatas('natal');
        $astroData = json_decode(json_encode($astroData), true);
        $arr = explode(':', $basis);
        // 检测用户当前档案信息
        switch ($arr[0])
        {
            case '1':
                // 行星落座
                $cond = $astroData['planets'][$arr[1]]['in_sign_id'];
                break;
            case '2':
                // 行星落宫
                $cond = $astroData['planets'][$arr[1]]['in_house_id'];
                break;
            case '3':
                // 宫主星飞宫
                $cond = $astroData['houses'][$arr[1]]['protect_inhouse_id'];
                break;
            case '4':
                // 宫头星座
                $cond = $astroData['houses'][$arr[1]]['in_sign_id'];
                break;
            default:
                $cond = '1';
                break;
        }

        return $basis . '-' . $cond;
    }

    /**
     * 是否是 IOS
     * @return bool
     */
    public static function IsIos(): bool
    {
        if (defined('IS_IOS'))
        {
            return IS_IOS;
        } else
        {
            $header = request()->header();
            $isIos = (isset($header['app-type']) && $header['app-type'] === 'ios');
            define('IS_IOS', $isIos);
            return IS_IOS;
        }
    }


    /**
     * 生成uuid 662C29CF-49BD-B1F9-5676-A8A0890EE2E2
     * @param string $str
     * @param int $startLength
     * @return string
     * @throws Exception
     */
    public static function encryptUuid(string $str, int $startLength = 8): string
    {
        $uuid = base_convert($str, 10, 16);
        $uuid = strlen($str) . strlen($uuid) . $uuid;

        $uuid2 = base_convert($str, 10, 16);
        $uuid2 = substr($uuid2, 1, 32 - strlen($uuid));
        $uuid = strtoupper($uuid . $uuid2);
        return substr($uuid, 0, $startLength) . '-' . substr($uuid, $startLength, 4) . '-' . substr($uuid, $startLength + 4, 4) . '-' . substr($uuid, $startLength + 8, 4) . '-' . substr($uuid, $startLength + 12);
    }

    /**
     * 解密uuid
     * @param string $uuid
     * @return string
     */
    public static function decryptUuid(string $uuid): string
    {
        $uuid = str_replace('-', '', $uuid);
        $uuidLen = substr($uuid, 2, 2);
        $str = base_convert(substr($uuid, 4, $uuidLen), 16, 10);
        $strLen = substr($uuid, 0, 2);
        if (strlen($str) != $strLen)
        {
            return sprintf("%0{$strLen}d", $str);
        }
        return $str;
    }

    /**
     * 秒转分钟
     * @param int $second
     * @return string
     */
    public static function secondToMinute(int $second)
    {
        $minute = (int)(floor($second / 60));
        $sec = $second % 60;

        return ($minute < 10 ? "0{$minute}:" : "{$minute}:") . ($sec < 10 ? "0{$sec}" : $sec);
    }

    /**
     * 是否 合规显示  2.9.12版本以上
     * @return bool  true 合规显示   false 不合规显示
     * @throws \think\Exception
     */
    public static function AppIsPassShowV1(): bool
    {
        $header = request()->header();
        if (defined('APP_IS_PASS_SHOW'))
        {
            return APP_IS_PASS_SHOW;
        }
        if (self::CheckAppVersion('2.9.12') !== '小')
        {
            if (!defined('USERINFO_UID'))
            {
                if (!empty($header['access-token']))
                {
                    $accessUserToken = (new Aes())->decryptWithOpenssl($header['access-token']);
                    if (!preg_match('/||/', $accessUserToken))
                    {
                        return true;
                    }
                    [$token, $uid] = explode('||', $accessUserToken);
                    if (!$uid)
                    {
                        return true;
                    }
                    define('USERINFO_UID', $uid);
                } else
                {
                    // 未登录
                    return true;
                }
            }

            $huaweiCheckData = (new AppConfig())->configInfo('app');
            switch ($header['channel'])
            {
                case 'huawei':
                    if (isset($huaweiCheckData['huawei_hg_version']) && $huaweiCheckData['huawei_hg_version'] && self::CheckAppVersion($huaweiCheckData['huawei_hg_version']) === '同')
                    {   // 华为 全网合规版本号
                        define('APP_IS_PASS_SHOW', true);
                        return true;
                    }
                    break;
                case 'honor':
                    if (isset($huaweiCheckData['honor_hg_version']) && $huaweiCheckData['honor_hg_version'] && self::CheckAppVersion($huaweiCheckData['honor_hg_version']) === '同')
                    {   // 华为 全网合规版本号
                        define('APP_IS_PASS_SHOW', true);
                        return true;
                    }
                    break;
                case 'OPPO':
                    if (isset($huaweiCheckData['oppo_hg_version']) && $huaweiCheckData['oppo_hg_version'] && self::CheckAppVersion($huaweiCheckData['oppo_hg_version']) === '同')
                    {   // 华为 全网合规版本号
                        define('APP_IS_PASS_SHOW', true);
                        return true;
                    }
                    break;
            }

            $uid = USERINFO_UID;

            // if ((int)$uid !== 13477830 )
            if (!in_array((int)$uid, [13477830, 18726758], true))
            {
                if ((self::CheckAppVersion('2.10.0') !== '小' && self::CheckAppVersion('2.14.0') === '小' && !in_array($header['channel'], ['honor', 'huawei'], true)) || (self::CheckAppVersion('2.14.0') !== '小' && !in_array($header['channel'], ['honor', 'huawei', 'OPPO'], true)))
                {   // 版本号 >= 2.10.0 并且 不是华为
                    $appConfig = (new AppConfig())->configInfo('app');
                    if (!isset($appConfig['is_lawful']) || !$appConfig['is_lawful'])
                    {
                        define('APP_IS_PASS_SHOW', false);
                        return false;
                    }
                }
                $orderNum = Cache::get('user:order_num' . $uid, false);
                if ($orderNum === false)
                {
                    $orderNum = Db::table('wechat_backup_07_18_14_43_00.app_orders')->where('uid', $uid)->whereIn('status', [1, 4])->count();
                    // 2分钟有效期
                    Cache::set('user:order_num' . $uid, $orderNum, 120);
                }
                $appIsPassShow = $orderNum > 0 ? false : true;
                if ($appIsPassShow)
                {
                    if (isset($header['install-time']))
                    {
                        $appIsPassShow = time() - $header['install-time'] > 86400 ? false : true;
                    } else
                    {
                        $appIsPassShow = true;
                    }
                }
            } else
            {
                $appIsPassShow = true;
            }
        } else
        {
            $appIsPassShow = false;
        }
        define('APP_IS_PASS_SHOW', $appIsPassShow);
        return APP_IS_PASS_SHOW;
    }


    /**
     * 档案备份 准有好事 -------------------------
     * @param int $zhunleUid 准了用户UID
     * @param int $uid 准有好事uid
     * @return array
     */
    public static function ZyhsRecordSync(int $zhunleUid, int $uid)
    {
        try
        {
            $selfRid = 0;
            $recodes = Db::table('wechat_backup_07_18_14_43_00.app_recode')->where(['uid' => $zhunleUid, 'status' => 1])
                ->column('isself,name,type,year,month,day,hour,minute,timezone,birth_country,birth_province,birth_city,live_country,live_province,live_city,comments,long_deg,long_min,ew,lat_deg,lat_min,ns,is_summer,avatar,from,or_day', 'id');

            $rids = array_column($recodes, 'id');
            $recodesAstro = Db::table('wechat_backup_07_18_14_43_00.app_recode_astro')->whereIn('rid', $rids)->column('sun,moon,merc,venu,mars,jupi,satu,non,lili,juno,asc,mc', 'rid');

            $recodeModel = new ZyhsRecode();
            $recodeAstroModel = new ZyhsRecodeAstro();
            $newTime = time();
            foreach ($recodes as $oldRid => $recodeItem)
            {
                unset($recodeItem['id']);
                $recodeItem['uid'] = $uid;
                $recodeItem['status'] = 1;
                $recodeItem['is_read'] = 0;
                $recodeItem['add_type'] = 4;    // 账号同步
                $recodeItem['channel'] = 'SYNC';
                if ($recodeItem['avatar'])
                {
                    $recodeItem['avatar'] = self::getImgUrl($recodeItem['avatar']);
                }
                $newRid = $recodeModel->insertGetId($recodeItem);
                $newRid = (int)$newRid;

                $recodeAstroItem = $recodesAstro[$oldRid];
                $recodeAstroItem['rid'] = $newRid;
                $recodeAstroItem['create_time'] = $newTime;
                $aid = $recodeAstroModel->insertGetId($recodeAstroItem);
                // 更新档案行星信息
                $recodeModel->UpdateData($uid, $newRid, ['aid' => $aid]);

                if ((int)$recodeItem['isself'] === 1)
                {   // 更新用户自己的信息
                    $selfRid = $newRid;
                    $d = ['has_self_recode' => 1];
                    if ($recodeItem['name'])
                    {
                        $d['nickname'] = $recodeItem['name'];
                    }
                    if ($recodeItem['avatar'])
                    {
                        $d['avatar'] = $recodeItem['avatar'];
                    }
                    // 更新用户信息
                    (new ZyhsUser())->updateUserinfo(['id' => $uid], $d);
                }
            }
        }
        catch (\Exception $e)
        {
            Log::write('Util model => recordSync()异常：' . $e->getMessage() . '失败行数：' . $e->getLine(), 'error');
            return [
                'status' => false,
            ];
        }
        return [
            'status' => true,
            'num' => count($recodes),
            'self_rid' => $selfRid,
        ];
    }

    /**
     * 准有好事 - 创建问答咨询ID
     * @return string
     */
    public static function ZyhsCreateQuestionAnswerId(): string
    {
        $time = date('YmdHis');
        return (string)($time - 20230880000000) . (string)(random_int(1000, 9999));
    }

    /**
     * 发票Ocr 识别
     * https://market.aliyun.com/apimarket/detail/cmapi00051549?spm=5176.2020520132.101.5.19057218Ock76R
     * @return array
     */
    public static function aliInvoiceOcr(string $invoiceUrl)
    {
        $file_extension = pathinfo($invoiceUrl, PATHINFO_EXTENSION);
        $isPdf = in_array($file_extension, ['pdf', 'PDF'], true);
        $file_extension = file_get_contents(Upload::getOssPrivateUrl($invoiceUrl));
        $imgBase64 = '';
        $pdfBase64 = '';
        if ($isPdf)
        {
            $pdfBase64 = urlencode(base64_encode($file_extension));
        } else
        {
            $imgBase64 = urlencode(base64_encode($file_extension));
        }
        $host = "https://juminvoie.market.alicloudapi.com";
        $path = "/ocr/invoice-v2";
        $appcode = "7dd1391c76eb403299b4afba18aabe59";
        $headers = [];
        array_push($headers, "Authorization:APPCODE " . $appcode);
        //根据API的要求，定义相对应的Content-Type
        array_push($headers, "Content-Type" . ":" . "application/x-www-form-urlencoded;charset=UTF-8");
        $bodys = "base64={$imgBase64}&pdfBase64={$pdfBase64}&pdfFileNum=1&type=normal&url=";
        $url = $host . $path;

        $resData = self::curlRequest('POST', $url, $bodys, $headers);

        return json_decode($resData, true);
    }

    /**
     * 发票 查验
     * https://market.aliyun.com/apimarket/detail/cmapi00060593?spm=5176.product-detail.detail.4.525e4f5ccSBt9G&endpoint=b65aaf7f98724315b8a8960c32266524
     * @param string $fpdm 发票代码 【注意：全电票可不传，其他必传】
     * @param string $fphm 发票号码
     * @param string $kprq 开票日期 格式YYYYMMDD
     * @param string $xym 校验码 【注意：专票、全电票可不传，其他必填。校验码支持全位和后6位】
     * @param string $bhsje 不含税金额 【注意：普票可不传，其他发票必填 。全电票请传含税金额，其他发票需传 不含税金额】
     * @return array
     */
    public static function aliIinvoiceCheck(string $fpdm, string $fphm, string $kprq, string $xym = '', string $bhsje = '')
    {
        $host = "https://jminvceins.market.alicloudapi.com";
        $path = "/invoice/query_validate";
        $appcode = "7dd1391c76eb403299b4afba18aabe59";
        $headers = [];
        array_push($headers, "Authorization:APPCODE " . $appcode);
        //根据API的要求，定义相对应的Content-Type
        array_push($headers, "Content-Type" . ":" . "application/x-www-form-urlencoded; charset=UTF-8");
        $bodys = "fpdm={$fpdm}&fphm={$fphm}&kprq={$kprq}&xym={$xym}&bhsje={$bhsje}";
        $url = $host . $path;

        $resData = self::curlRequest('POST', $url, $bodys, $headers);

        return json_decode($resData, true);
    }

    //计算 应缴个税
    public static function CalculationTaxRate($amount)
    {
        $amount1 = 0; //应缴纳所得额
        //应缴纳所得额计算： 收入800元内不扣钱，收入4000元（包含4000）以下-800 ，收入4000元以上*80%
        //劳务所得税：不超过20000*20%，速算扣除0
        //超过20000*30%，速算扣除数-2000
        //超过50000*40%，速算扣除数-7000
        if ($amount < 800)
        {
            $amount1 = 0;
        } else if ($amount <= 4000)
        {
            $amount1 = $amount - 800;
        } else if ($amount > 4000)
        {
            $amount1 = $amount * 0.8;
        }
        $personalIncomeTax = 0; // 应缴税额
        if ($amount1 < 20000)
        {
            $personalIncomeTax = sprintf("%.2f", $amount1 * 0.2);
        } else if ($amount1 >= 20000)
        {
            $personalIncomeTax = sprintf("%.2f", ($amount1 * 0.3 - 2000));
        } else if ($amount1 > 50000)
        {
            $personalIncomeTax = sprintf("%.2f", ($amount1 * 0.4 - 7000));
        }

        return $personalIncomeTax;
    }

    /**
     * 准有好事 是否是华为或荣耀 并且 为合规版本
     * @param int $appType 1=>准有好事 2=>准了
     * @return bool true|false  true 合规显示  false 不合规显示
     * @throws \Exception
     */
    public static function ZyhsIsHuaweiOrHonorHg(int $appType = 1): bool
    {
        if (defined('IS_HUAWEI_OR_HONOR_HG'))
        {
            return IS_HUAWEI_OR_HONOR_HG;
        } else
        {
            $header = request()->header();
            $isHuaweiOrHonor = isset($header['channel']) && in_array($header['channel'], ['honor', 'huawei'], true);

            if ($isHuaweiOrHonor)
            {   // 荣耀 手机
                $testAccountConfig = $appType === 1 ? (new ZyhsConfig())->configInfo('rtc') : (new AppConfig())->configInfo('app');;
                switch ($header['channel'])
                {
                    case 'huawei':
                        if (!(isset($testAccountConfig['huawei_hg_version']) && $testAccountConfig['huawei_hg_version'] && $testAccountConfig['huawei_hg_version'] === $header['version']))
                        {
                            $isHuaweiOrHonor = false;
                        }
                        break;
                    case 'honor':
                        if (!(isset($testAccountConfig['honor_hg_version']) && $testAccountConfig['honor_hg_version'] && $testAccountConfig['honor_hg_version'] === $header['version']))
                        {
                            $isHuaweiOrHonor = false;
                        }
                        break;
                    default:
                }
            }

            define('IS_HUAWEI_OR_HONOR_HG', $isHuaweiOrHonor);
            return IS_HUAWEI_OR_HONOR_HG;
        }
    }

    /**
     * 准有好事 是否是华为或荣耀或OPPO 并且 为合规版本
     * @param int $appType 1=>准有好事 2=>准了
     * @return bool
     * @throws \Exception
     */
    public static function ZyhsIsHuaweiOrHonorOrOppoHg(int $appType = 1): bool
    {
        if (defined('IS_HUAWEI_OR_HONOR_OR_OPPO_HG'))
        {
            return IS_HUAWEI_OR_HONOR_OR_OPPO_HG;
        } else
        {
            $header = request()->header();
            $isHuaweiOrHonorOrOppo = isset($header['channel']) && in_array($header['channel'], ['honor', 'huawei', 'OPPO'], true);

            if ($isHuaweiOrHonorOrOppo)
            {   // 荣耀 手机
                $testAccountConfig = $appType === 1 ? (new ZyhsConfig())->configInfo('rtc') : (new AppConfig())->configInfo('app');;
                switch ($header['channel'])
                {
                    case 'huawei':
                        if (!(isset($testAccountConfig['huawei_hg_version']) && $testAccountConfig['huawei_hg_version'] && $testAccountConfig['huawei_hg_version'] === $header['version']))
                        {
                            $isHuaweiOrHonorOrOppo = false;
                        }
                        break;
                    case 'OPPO':
                        if (!(isset($testAccountConfig['oppo_hg_version']) && $testAccountConfig['oppo_hg_version'] && $testAccountConfig['oppo_hg_version'] === $header['version']))
                        {
                            $isHuaweiOrHonorOrOppo = false;
                        }
                        break;
                    case 'honor':
                        if (!(isset($testAccountConfig['honor_hg_version']) && $testAccountConfig['honor_hg_version'] && $testAccountConfig['honor_hg_version'] === $header['version']))
                        {
                            $isHuaweiOrHonorOrOppo = false;
                        }
                        break;
                    default:
                }
            }

            define('IS_HUAWEI_OR_HONOR_OR_OPPO_HG', $isHuaweiOrHonorOrOppo);
            return IS_HUAWEI_OR_HONOR_OR_OPPO_HG;
        }
    }

    public static function IsHuaweiOrHonorOrOppo(): bool
    {
        if (defined('IS_HUAWEI_OR_HONOR_OR_OPPO'))
        {
            return IS_HUAWEI_OR_HONOR_OR_OPPO;
        } else
        {
            $header = request()->header();
            $isHuaweiOrHonorOrOppo = isset($header['channel']) && in_array($header['channel'], ['honor', 'huawei', 'OPPO'], true);

            define('IS_HUAWEI_OR_HONOR_OR_OPPO', $isHuaweiOrHonorOrOppo);
            return IS_HUAWEI_OR_HONOR_OR_OPPO;
        }
    }

    /**
     * 是否是荣耀
     * @return bool
     * @throws Exception
     */
    public static function IsHonor(): bool
    {
        if (defined('IS_HONOR'))
        {
            return IS_HONOR;
        } else
        {
            $header = request()->header();
            $isHonor = (isset($header['channel']) && $header['channel'] === 'honor');

            define('IS_HONOR', $isHonor);
            return IS_HONOR;
        }
    }


    /**
     * 是否是华为 或者 荣耀
     * @return bool
     * @throws Exception
     */
    public static function IsHuaweiOrHonor(): bool
    {
        if (defined('IS_HUAWEI_OR_HONOR'))
        {
            return IS_HUAWEI_OR_HONOR;
        } else
        {
            $header = request()->header();
            $isHuaweiOrHonor = isset($header['channel']) && in_array($header['channel'], ['honor', 'huawei'], true);
            define('IS_HUAWEI_OR_HONOR', $isHuaweiOrHonor);
            return IS_HUAWEI_OR_HONOR;
        }
    }

    /**
     * 获取 准了 连线咨询福利 弹窗提示信息
     * @param int $freeMin
     * @return array|null
     */
    public static function getZhunleConsultBenefitsConfig(int $freeMin): ?array
    {
        $imgConfig = [
            3 => '/test/consult_chat/zl_consult_free_3min.png',
            4 => '/test/consult_chat/zl_consult_free_4min.png',
            5 => '/test/consult_chat/zl_consult_free_5min.png',
        ];
        if (isset($imgConfig[$freeMin]))
        {
            return [
                'img' => $imgConfig[$freeMin],
                'target' => 'local',
                'link' => 'consult_astr_ask?need_auth=2',
            ];
        } else
        {
            return null;
        }
    }

    public static function getZhunleQuestionAnswerConfig()
    {
        $goodInfoConfig = config('zlcr2_question.add_question');
        $goodInfo = (new Goods())->GoodsTypeInfo(22);

        foreach ($goodInfo as $gid => $item)
        {
            $goodInfo[$gid]['price'] = $item['now_prize'];

            if (!$item['is_add'] && isset($goodInfoConfig[$gid]))
            {
                $goodInfo[$gid]['add_price'] = $goodInfo[$goodInfoConfig[$gid]]['now_prize'];
            }
        }

        return $goodInfo;
    }
}
